-
1
require "bootstrap/component_loader"
-
1
require "bootstrap/component"
-
-
1
class Bootstrap
-
1
include Delegate
-
1
extend ComponentLoader
-
1
load_components
-
-
1
def initialize context=nil
-
4
@context = context
-
end
-
-
1
def render *args, &block
-
instance_exec *args, &block
-
end
-
end
-
# require 'component'
-
-
1
class Bootstrap
-
1
module BasicTags
-
1
def html content
-
add_content String(content).html_safe
-
""
-
end
-
-
1
Component.def_div_method :div, nil do |opts, extra_args|
-
prepend_class opts, extra_args.first if extra_args.present?
-
opts
-
end
-
-
1
Component.def_div_method :span, nil do |opts, extra_args|
-
prepend_class opts, extra_args.first if extra_args.present?
-
opts
-
end
-
-
1
Component.def_tag_method :tag, nil, tag: :yield do |opts, extra_args|
-
prepend_class opts, extra_args[1] if extra_args[1].present?
-
opts[:tag] = extra_args[0]
-
opts
-
end
-
end
-
end
-
#! no set module
-
1
class Bootstrap
-
1
class TagMethod
-
1
def initialize component, name, html_class, tag_opts={}, &tag_block
-
@component = component
-
@name = name
-
@html_class = html_class
-
@tag_opts = tag_opts
-
@tag_block = tag_block
-
@append = []
-
@wrap = []
-
@xm = Builder::XmlMarkup.new
-
end
-
-
1
def call *args, &content_block
-
component.content.push "".html_safe
-
-
content, opts = content_block.call
-
wrappers = @wrap.pop
-
if wrappers.present?
-
while wrappers.present? do
-
wrapper = wrappers.shift
-
if wrapper.is_a? Symbol
-
send wrapper, &content_block
-
else
-
instance_exec(content, &wrappers.shift)
-
end
-
end
-
else
-
add_content content
-
end
-
-
collected_content = @content.pop
-
tag_name = opts.delete(:tag) if tag_name == :yield
-
add_content content_tag(tag_name, collected_content, opts, false)
-
@append.pop.each do |block|
-
add_content instance_exec(&block)
-
end
-
""
-
end
-
-
1
def method_missing method, *args, &block
-
@component.send method, *args, &block
-
end
-
-
1
def prepend &block
-
tmp = @content.pop
-
instance_exec &block
-
@content << tmp
-
end
-
-
1
def wrap &block
-
instance_exec &block
-
end
-
-
1
def append &block
-
@append[-1] << block
-
end
-
-
1
def wrapInner tag=nil, &block
-
@wrap[-1] << (block_given? ? block : tag)
-
end
-
end
-
-
1
class Component
-
1
def initialize context, *args, &block
-
@context = context
-
@content = ["".html_safe]
-
@args = args
-
@child_args = []
-
@append = []
-
@wrap = []
-
@build_block = block
-
@html = Builder::XmlMarkup.new
-
end
-
-
1
class << self
-
1
def render format, *args, &block
-
new(format, *args, &block).render
-
end
-
-
# Like def_tag_method but always generates a div tag
-
# The tag option is not available
-
1
def def_div_method name, html_class, opts={}, &tag_block
-
4
def_tag_method name, html_class, opts.merge(tag: :div), &tag_block
-
end
-
-
# Defines a method that generates a html tag
-
# @param method_name [Symbol, String] the name of the method. If no :tag option in tag_opts is defined then the name is also the name of the tag that the method generates
-
# @param html_class [String] a html class that is added to tag. Use nil if you don't want a html_class
-
# @param tag_opts [Hash] additional argument that will be added to the tag
-
# @option tag_opts [Symbol, String] tag the name of the tag
-
# @example
-
# def_tag_method :link, "known-link", tag: :a, id: "uniq-link"
-
# link # => <a class="known-link" id="uniq-link"></a>
-
1
def def_tag_method method_name, html_class, tag_opts={}, &tag_opts_block
-
8
tag = tag_opts.delete(:tag) || method_name
-
8
return def_simple_tag_method method_name, tag, html_class, tag_opts unless block_given?
-
-
5
define_method method_name do |*args, &content_block|
-
content, opts, new_child_args = standardize_args args, &tag_opts_block
-
add_classes opts, html_class, tag_opts.delete(:optional_classes)
-
-
@html.tag! tag, opts do
-
instance_exec &content_block
-
end
-
end
-
end
-
-
1
def def_simple_tag_method method_name, tag, html_class, tag_opts={}
-
3
define_method method_name do |*args, &content_block|
-
@html.tag! tag, class: html_class do
-
instance_exec &content_block
-
end
-
end
-
end
-
end
-
-
-
1
def render
-
@rendered = begin
-
render_content
-
# @content[-1]
-
end
-
end
-
-
1
def prepend &block
-
tmp = @content.pop
-
instance_exec &block
-
@content << tmp
-
end
-
-
1
def insert &block
-
instance_exec &block
-
end
-
-
1
def append &block
-
@append[-1] << block
-
end
-
-
1
def wrap tag=nil, &block
-
@wrap[-1] << (block_given? ? block : tag)
-
end
-
-
1
private
-
-
1
def render_content
-
# if @build_block.arity > 0
-
instance_exec *@args, &@build_block
-
end
-
-
1
def generate_content content, processor, &block
-
content = instance_exec &block if block.present?
-
return content if !processor || !content.is_a?(Array)
-
content.each {|item| send processor, item}
-
""
-
end
-
-
1
def with_child_args args
-
@child_args << args if args.present?
-
res = yield
-
@child_args.pop if args.present?
-
res
-
end
-
-
1
def add_content content
-
@content[-1] << "\n#{content}".html_safe if content.present?
-
end
-
-
1
def process_tag tag_name, &content_block
-
end
-
-
1
def standardize_args args, &block
-
opts = args.last.is_a?(Hash) ? args.pop : {}
-
items = ((args.one? && args.last.is_a?(String)) || args.last.is_a?(Array)) &&
-
args.pop
-
if block.present?
-
opts, args = instance_exec opts, args, &block
-
unless opts.is_a?(Hash)
-
raise Card::Error, "first return value of a tag block has to be a hash"
-
end
-
end
-
-
[items, opts, args]
-
end
-
-
1
def add_classes opts, html_class, optional_classes
-
prepend_class opts, html_class if html_class
-
Array.wrap(optional_classes).each do |k, v|
-
prepend_class opts, v if opts.delete k
-
end
-
end
-
-
1
include BasicTags
-
1
include Delegate
-
end
-
end
-
1
class Bootstrap
-
1
class Component
-
1
class Carousel < Component
-
1
def render_content
-
carousel *@args, &@build_block
-
end
-
-
1
def carousel id, active_index, &block
-
@id = id
-
@active_item_index = active_index
-
@items = []
-
instance_exec &block
-
-
@html.div class: "carousel slide", id: id, "data-ride" => "carousel" do
-
indicators
-
items
-
control_prev
-
control_next
-
end
-
end
-
-
1
def item content=nil, &block
-
@items << (content || block)
-
end
-
-
1
def items
-
@html.div class: "carousel-inner", role: "listbox" do
-
@items.each_with_index do |item, index|
-
html_opts = { class: "carousel-item" }
-
add_class html_opts, "active" if index == @active_item_index
-
@html.div html_opts do
-
item = item.call if item.respond_to?(:call)
-
@html << item if item.is_a?(String)
-
end
-
end
-
end
-
end
-
-
1
def control_prev
-
@html.a class: "carousel-control-prev", href: "##{@id}", role: "button",
-
"data-slide" => "prev" do
-
@html.span class: "carousel-control-prev-icon", "aria-hidden" => "true"
-
@html.span "Previous", class: "sr-only"
-
end
-
end
-
-
1
def control_next
-
@html.a class: "carousel-control-next", href: "##{@id}", role: "button",
-
"data-slide": "next" do
-
@html.span class: "carousel-control-next-icon", "aria-hidden" => "true"
-
@html.span "Next", class: "sr-only"
-
end
-
end
-
-
1
def indicators
-
@html.ol class: "carousel-indicators" do
-
@items.size.times { |i| indicator i }
-
end
-
end
-
-
1
def indicator index
-
html_opts = { "data-slide-to" => index, "data-target": "##{@id}" }
-
add_class html_opts, "active" if index == @active_item_index
-
@html.li html_opts
-
end
-
end
-
end
-
end
-
1
class Bootstrap
-
1
class Component
-
1
class Form < Component
-
1
def render_content *args
-
form *args, &@build_block
-
end
-
-
#
-
# def_tag_method :form, nil, optional_classes: {
-
# horizontal: "form-horizontal",
-
# inline: "form-inline"
-
# }
-
# def_div_method :group, "form-group"
-
# def_tag_method :label, nil
-
# def_tag_method :input, "form-control" do |opts, extra_args|
-
# type, label = extra_args
-
# prepend { label label, for: opts[:id] } if label
-
# opts[:type] = type
-
# opts
-
# end
-
-
1
def form opts={}, &block
-
add_class opts, "form-horizontal" if opts.delete(:horizontal)
-
add_class opts, "form-inline" if opts.delete(:inline)
-
@html.form opts do
-
instance_exec &block
-
end
-
end
-
-
1
def group text=nil, &block
-
@html.div text, class: "form-group" do
-
instance_exec &block
-
end
-
end
-
-
1
def label text=nil, &block
-
@html.label text, &block
-
end
-
-
1
def input type, text: nil, label: nil, id: nil
-
@html.input id: id, class: "form-control", type: type do
-
@html.label label, for: id if label
-
@html << text if text
-
end
-
end
-
-
1
%i[text password datetime datetime-local date month time
-
week number email url search tel color].each do |tag|
-
# def_tag_method tag, "form-control", attributes: { type: tag },
-
# tag: :input do |opts, extra_args|
-
# label, = extra_args
-
# prepend { label label, for: opts[:id] } if label
-
# opts
-
# end
-
-
14
define_method tag do |text: nil, id:, label: |
-
@html.input id: id, class: "form-control", type: tag do
-
@html.label label, for: id if label
-
@html << text
-
end
-
end
-
end
-
end
-
end
-
end
-
1
class Bootstrap
-
1
class Component
-
1
class HorizontalForm < Form
-
1
def left_col_width
-
@child_args.last && @child_args.last[0] || 2
-
end
-
-
1
def right_col_width
-
@child_args.last && @child_args.last[1] || 10
-
end
-
-
1
def_tag_method :form, "form-horizontal"
-
-
1
def_tag_method :label, "control-label" do |opts, _extra_args|
-
prepend_class opts, "col-sm-#{left_col_width}"
-
opts
-
end
-
-
# def_div_method :input, nil do |opts, extra_args, &block|
-
# type, label = extra_args
-
# prepend { tag(:label, nil, for: opts[:id]) { label } } if label
-
# insert { inner_input opts.merge(type: type) }
-
# { class: "col-sm-#{right_col_width}" }
-
# end
-
-
1
def label_col label, id:
-
@html.label label, for: id, class: "col-sm-#{left_col_width} control-label"
-
end
-
-
1
def input type, label:, id:, &block
-
label_col label, id: id
-
@html.div class: "col-sm-#{right_col_width}" do
-
@html.input type: type, id: id, class: "form-control"
-
end
-
# block.call class: "col-sm-#{right_col_width}" do
-
# inner_input opts.merge(type: type)
-
# end
-
end
-
-
1
def_tag_method :inner_input, "form-control", tag: :input
-
1
def_div_method :inner_checkbox, "checkbox"
-
-
1
def_div_method :checkbox, nil do |opts, extra_args|
-
inner_checkbox do
-
label do
-
inner_input "checkbox", extra_args.first, opts
-
end
-
end
-
{ class: "col-sm-offset-#{left_col_width} col-sm-#{right_col_width}" }
-
end
-
-
1
def checkbox text, extra_args
-
@html.div class: "col-sm-offset-#{left_col_width} col-sm-#{right_col_width}" do
-
@html.div class: "checkbox" do
-
label_cllabel do
-
inner_input "checkbox"
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
1
class Bootstrap
-
1
class Component
-
# generate bootstrap column layout
-
# @example
-
# layout container: true, fluid: true, class: "hidden" do
-
# row 6, 6, class: "unicorn" do
-
# column "horn",
-
# column "rainbow", class: "colorful"
-
# end
-
# end
-
# @example
-
# layout do
-
# row 3, 3, 4, 2, class: "unicorn" do
-
# [ "horn", "body", "tail", "rainbow"]
-
# end
-
# add_html "<span> some extra html</span>"
-
# row 6, 6, ["unicorn", "rainbow"], class: "horn"
-
# end
-
1
class Layout < OldComponent
-
-
1
def render
-
@rendered = begin
-
4
render_content
-
4
@content[-1]
-
end
-
end
-
-
1
def render_content
-
4
content = instance_exec *@args, &@build_block
-
4
add_content content
-
4
opts = @args.first
-
4
return unless opts && opts.delete(:container)
-
2
content = @content.pop
-
2
@content = ["".html_safe]
-
2
container content, opts
-
end
-
-
1
add_div_method :container, nil do |opts, _extra_args|
-
2
prepend_class opts, opts.delete(:fluid) ? "container-fluid" : "container"
-
2
opts
-
end
-
-
# @param args column widths, column content and html attributes
-
# @example
-
# row 6, 6, ["col one", "col two"], class: "count", id: "count"
-
# @example
-
# row md: 12, xs: 8, "single column content"
-
# @example
-
# row md: [1, 11], xs: [2, 10] do
-
# col "A"
-
# col "B"
-
# end
-
1
add_div_method :row, "row", content_processor: :column do |opts, extra_args|
-
4
cols_content = extra_args.pop if extra_args.last.is_a? Array
-
4
[opts, col_widths(extra_args, opts), cols_content].compact
-
end
-
-
# default column width type is for medium devices (col-md-)
-
1
add_div_method :column, nil do |opts, _extra_args|
-
4
@child_args.last.each do |medium, size|
-
4
if medium == :xs
-
4
prepend_class opts, "col-#{size.shift}"
-
else
-
prepend_class opts, "col-#{medium}-#{size.shift}"
-
end
-
end
-
4
opts
-
end
-
-
1
alias_method :col, :column
-
-
1
private
-
-
1
def standardize_row_args args
-
opts = args.last.is_a?(Hash) ? args.pop : {}
-
cols = (args.last.is_a?(Array) || args.last.is_a?(String)) &&
-
Array.wrap(args.pop)
-
[cols, opts, col_widths(args, opts)]
-
end
-
-
1
def col_widths args, opts
-
4
opts = args.pop if args.one? && args.last.is_a?(Hash)
-
4
if args.present?
-
raise Error, "bad argument" unless args.all? { |a| a.is_a? Integer }
-
{ md: Array.wrap(args) }
-
else
-
4
%i[lg xs sm md].each_with_object({}) do |k, cols_w|
-
16
next unless (widths = opts.delete(k))
-
2
cols_w[k] = Array.wrap widths
-
end
-
end
-
end
-
end
-
end
-
end
-
1
class Bootstrap
-
1
class Component
-
1
class Panel < OldComponent
-
1
def_div_method :panel, "card"
-
1
def_div_method :heading, "card-header"
-
1
def_div_method :body, "card-body"
-
end
-
end
-
end
-
1
class Bootstrap
-
1
module ComponentLoader
-
1
def load_components
-
1
components.each do |component|
-
5
require_relative "component/#{component}"
-
5
include_component component
-
end
-
end
-
-
1
def include_component component
-
5
component_class = to_const component.camelcase
-
5
define_method component do |*args, &block|
-
6
component_class.render self, *args, &block
-
end
-
end
-
-
1
def components
-
2
path = File.expand_path "../component/*.rb", __FILE__
-
2
Dir.glob(path).map do |file|
-
10
File.basename file, ".rb"
-
end
-
end
-
-
1
def to_const name
-
5
self.class.const_get "::Bootstrap::Component::#{name.camelcase}"
-
end
-
end
-
end
-
1
class Bootstrap
-
1
module Delegate
-
1
def method_missing method_name, *args, &block
-
# return super unless @context.respond_to? method_name
-
108
if block_given?
-
@context.send(method_name, *args, &block)
-
else
-
108
@context.send(method_name, *args)
-
end
-
end
-
-
1
def respond_to_missing? method_name, _include_private=false
-
@context.respond_to? method_name
-
end
-
end
-
end
-
#! no set module
-
1
class Bootstrap
-
1
class OldComponent < Component
-
1
def initialize context, *args, &block
-
6
@context = context
-
6
@content = ["".html_safe]
-
6
@args = args
-
6
@child_args = []
-
6
@append = []
-
6
@wrap = []
-
6
@build_block = block
-
end
-
-
1
class << self
-
1
def render format, *args, &block
-
6
new(format, *args, &block).render
-
end
-
-
# Like add_tag_method but always generates a div tag
-
# The tag option is not available
-
1
def add_div_method name, html_class, opts={}, &tag_block
-
8
add_tag_method name, html_class, opts.merge(tag: :div), &tag_block
-
end
-
-
# Defines a method that generates a html tag
-
# @param name [Symbol, String] the name of the method. If no :tag option in tag_opts is defined then the name is also the name of the tag that the method generates
-
# @param html_class [String] a html class that is added to tag. Use nil if you don't want a html_class
-
# @param tag_opts [Hash] additional argument that will be added to the tag
-
# @option tag_opts [Symbol, String] tag the name of the tag
-
# @example
-
# add_tag_method :link, "known-link", tag: :a, id: "uniq-link"
-
# link # => <a class="known-link" id="uniq-link"></a>
-
1
def add_tag_method name, html_class, tag_opts={}, &tag_block
-
9
define_method name do |*args, &block|
-
20
process_tag tag_opts[:tag] || name do
-
20
content, opts, new_child_args = standardize_args args, &tag_block
-
20
add_classes opts, html_class, tag_opts.delete(:optional_classes)
-
20
if (attributes = tag_opts.delete(:attributes))
-
opts.merge! attributes
-
end
-
-
20
content = with_child_args new_child_args do
-
20
generate_content content,
-
tag_opts[:content_processor],
-
&block
-
end
-
-
20
[content, opts]
-
end
-
end
-
end
-
-
1
alias_method :def_div_method, :add_div_method
-
1
alias_method :def_tag_method, :add_tag_method
-
end
-
-
1
def render
-
@rendered = begin
-
2
render_content
-
2
@content[-1]
-
end
-
end
-
-
1
def prepend &block
-
tmp = @content.pop
-
instance_exec &block
-
@content << tmp
-
end
-
-
1
def insert &block
-
instance_exec &block
-
end
-
-
1
def append &block
-
@append[-1] << block
-
end
-
-
1
def wrap tag=nil, &block
-
@wrap[-1] << (block_given? ? block : tag)
-
end
-
-
1
private
-
-
1
def render_content
-
# if @build_block.arity > 0
-
2
instance_exec *@args, &@build_block
-
end
-
-
1
def generate_content content, processor, &block
-
20
content = instance_exec &block if block.present?
-
20
return content if !processor || !content.is_a?(Array)
-
content.each {|item| send processor, item}
-
""
-
end
-
-
1
def with_child_args args
-
20
@child_args << args if args.present?
-
20
res = yield
-
20
@child_args.pop if args.present?
-
20
res
-
end
-
-
1
def add_content content
-
48
@content[-1] << "\n#{content}".html_safe if content.present?
-
end
-
-
1
def process_tag tag_name, &content_block
-
20
@content.push "".html_safe
-
20
@append << []
-
20
@wrap << []
-
-
-
20
content, opts = content_block.call
-
20
wrappers = @wrap.pop
-
20
if wrappers.present?
-
while wrappers.present? do
-
wrapper = wrappers.shift
-
if wrapper.is_a? Symbol
-
send wrapper, &content_block
-
else
-
instance_exec(content, &wrappers.shift)
-
end
-
end
-
else
-
20
add_content content
-
end
-
-
20
collected_content = @content.pop
-
20
tag_name = opts.delete(:tag) if tag_name == :yield
-
20
add_content content_tag(tag_name, collected_content, opts, false)
-
20
@append.pop.each do |block|
-
add_content instance_exec(&block)
-
end
-
20
""
-
end
-
-
1
def standardize_args args, &block
-
20
opts = args.last.is_a?(Hash) ? args.pop : {}
-
20
items = ((args.one? && args.last.is_a?(String)) || args.last.is_a?(Array)) &&
-
args.pop
-
20
if block.present?
-
16
opts, args = instance_exec opts, args, &block
-
16
unless opts.is_a?(Hash)
-
raise Card::Error, "first return value of a tag block has to be a hash"
-
end
-
end
-
-
20
[items, opts, args]
-
end
-
-
1
def add_classes opts, html_class, optional_classes
-
20
prepend_class opts, html_class if html_class
-
20
Array.wrap(optional_classes).each do |k, v|
-
prepend_class opts, v if opts.delete k
-
end
-
end
-
-
# include BasicTags
-
1
def html content
-
4
add_content String(content).html_safe
-
4
""
-
end
-
-
1
add_div_method :div, nil do |opts, extra_args|
-
4
prepend_class opts, extra_args.first if extra_args.present?
-
4
opts
-
end
-
-
1
add_div_method :span, nil do |opts, extra_args|
-
prepend_class opts, extra_args.first if extra_args.present?
-
opts
-
end
-
-
1
add_tag_method :tag, nil, tag: :yield do |opts, extra_args|
-
2
prepend_class opts, extra_args[1] if extra_args[1].present?
-
2
opts[:tag] = extra_args[0]
-
2
opts
-
end
-
-
1
include Delegate
-
end
-
end
-
1
require "bootstrap"
-
1
module Bootstrapper
-
1
extend Bootstrap::ComponentLoader
-
-
1
def bootstrap
-
6
@bootstrap ||= ::Bootstrap.new(self)
-
end
-
-
1
def bs *args, &block
-
bootstrap.render *args, &block
-
end
-
-
1
components.each do |component|
-
5
delegate component, to: :bootstrap, prefix: :bs
-
end
-
end
-
1
class Card
-
1
class LazyTab < Tab
-
1
def url
-
8
@url ||= (config_hash? && @config[:path]) || format.path(view: view)
-
end
-
-
1
def view
-
4
@view ||= (config_hash? && @config[:view]) || @config
-
end
-
-
1
def tab_button
-
4
if url
-
4
super
-
else
-
wrap_with(:li, label, role: "presentation")
-
end
-
end
-
-
1
def button_attrib
-
12
@button_attrib ||= super.merge("data-url" => url.html_safe)
-
end
-
-
1
def tab_button_link
-
4
add_class button_attrib, "load" unless active?
-
4
super
-
end
-
-
1
def content
-
4
@content ||= ""
-
end
-
-
1
def tab_pane args=nil, &block
-
4
@content = yield if active? && block_given?
-
4
super
-
end
-
end
-
end
-
1
class Card
-
1
class Tab
-
1
attr_reader :format, :name, :label, :content, :button_attrib
-
-
1
class << self
-
1
def tab_objects format, tab_hash, active_name, klass=nil
-
2
klass ||= Card::Tab
-
2
active_name ||= tab_hash.keys.first
-
2
tab_hash.map do |name, config|
-
4
klass.new format, name, active_name, config
-
end
-
end
-
end
-
-
1
delegate :add_class, :wrap_with, :unique_id, :link_to, to: :format
-
-
1
def initialize format, name, active_name, config
-
4
@format = format
-
4
@name = name
-
4
@active_name = active_name
-
4
@config = config
-
end
-
-
1
def tab_button
-
4
add_class button_attrib, "active" if active?
-
4
wrap_with :li, tab_button_link,
-
role: :presentation,
-
class: "nav-item tab-li-#{name}"
-
end
-
-
1
def tab_pane args=nil
-
4
pane_attr = { role: :tabpanel, id: tab_id }
-
4
pane_attr.merge! args if args.present?
-
4
add_class pane_attr, "tab-pane tab-pane-#{name}"
-
4
add_class pane_attr, "active" if active?
-
4
wrap_with :div, content, pane_attr
-
end
-
-
1
private
-
-
1
def config_hash?
-
16
@config.is_a? Hash
-
end
-
-
1
def label
-
4
@label ||= (config_hash? && @config[:title]) || name
-
end
-
-
1
def content
-
@content ||= config_hash? ? @config[:content] : @config
-
end
-
-
1
def button_attrib
-
4
@button_attrib ||= (config_hash? && @config[:button_attr]) || {}
-
end
-
-
1
def tab_button_link
-
4
add_class button_attrib, "nav-link"
-
-
4
link_to label, button_attrib.merge(
-
path: "##{tab_id}",
-
role: "tab",
-
"data-toggle" => "tab",
-
"data-tab-name" => name
-
)
-
end
-
-
1
def tab_id
-
8
@tab_id ||= "#{unique_id}-#{name.to_name.safe_key}"
-
end
-
-
1
def active?
-
16
name == @active_name
-
end
-
end
-
end
-
1
require "carrierwave"
-
-
1
module CarrierWave
-
# adapt carrierwave mount to cards
-
1
module CardMount
-
1
include CarrierWave::Mount
-
-
1
def uploaders
-
2
Card.uploaders ||= {}
-
end
-
-
1
def uploader_options
-
2
Card.uploader_options ||= {}
-
end
-
-
1
def mount_uploader column, uploader=nil, options={}, &block
-
2
options[:mount_on] ||= :db_content
-
2
super
-
-
2
class_eval <<-RUBY, __FILE__, __LINE__ + 1
-
event :store_#{column}_event, :finalize,
-
on: :save, when: :store_#{column}_event? do
-
store_#{column}!
-
end
-
-
# remove files only if card has no history
-
event :remove_#{column}_event, :finalize,
-
on: :delete, when: proc { |c| !c.history? } do
-
remove_#{column}!
-
end
-
event :mark_remove_#{column}_false_event, :finalize,
-
on: :update do
-
mark_remove_#{column}_false
-
end
-
event :store_previous_changes_for_#{column}_event, :store,
-
on: :update, when: proc { |c| !c.history? } do
-
store_previous_changes_for_#{column}
-
end
-
event :remove_previously_stored_#{column}_event, :finalize,
-
on: :update, when: proc { |c| !c.history?} do
-
remove_previously_stored_#{column}
-
end
-
-
# don't attempt to store coded images unless ENV specifies it
-
def store_#{column}_event?
-
!coded? || ENV["STORE_CODED_FILES"]
-
end
-
-
def attachment
-
#{column}
-
end
-
-
def store_attachment!
-
store_#{column}!
-
end
-
-
def attachment_name
-
"#{column}".to_sym
-
end
-
-
def read_uploader *args
-
read_attribute *args
-
end
-
-
def write_uploader *args
-
write_attribute *args
-
end
-
-
def #{column}=(new_file)
-
return if new_file.blank?
-
assign_file(new_file) { super }
-
end
-
-
def remote_#{column}_url=(url)
-
assign_file(url) { super }
-
end
-
-
def assign_file file
-
db_column = _mounter(:#{column}).serialization_column
-
send(:"\#{db_column}_will_change!") # unless attribute_is_changing? db_column
-
if web?
-
self.content = file
-
else
-
send(:"#{column}_will_change!")
-
yield
-
end
-
end
-
-
def remove_#{column}=(value)
-
column = _mounter(:#{column}).serialization_column
-
send(:"\#{column}_will_change!")
-
super
-
end
-
-
def remove_#{column}!
-
self.remove_#{column} = true
-
write_#{column}_identifier
-
self.remove_#{column} = false
-
super
-
end
-
-
def #{column}_will_change!
-
@#{column}_changed = true
-
@#{column}_is_changing = true
-
end
-
-
def #{column}_is_changing?
-
@#{column}_is_changing
-
end
-
-
def #{column}_changed?
-
@#{column}_changed
-
end
-
-
def serializable_hash(options=nil)
-
hash = {}
-
-
except = options && options[:except] &&
-
Array.wrap(options[:except]).map(&:to_s)
-
only = options && options[:only] &&
-
Array.wrap(options[:only]).map(&:to_s)
-
-
self.class.uploaders.each do |column, uploader|
-
if (!only && !except) || (only && only.include?(column.to_s)) ||
-
(!only && except && !except.include?(column.to_s))
-
hash[column.to_s] = _mounter(column).uploader.serializable_hash
-
end
-
end
-
super(options).merge(hash)
-
end
-
RUBY
-
end
-
end
-
end
-
1
module CarrierWave
-
1
class << self
-
1
def tmp_path
-
220
@tmp_path ||= Card.paths["tmp"].existent.first
-
end
-
end
-
-
1
class SanitizedFile
-
1
def content_type
-
# the original content_type method doesn't seem to be very reliable
-
# It uses mime_magic_content_type - which returns invalid/invalid for css files
-
# that start with a comment - as the second option. (we switch the order and
-
# use it as the third option)
-
727
@content_type ||=
-
existing_content_type ||
-
mini_mime_content_type ||
-
mime_magic_content_type
-
end
-
end
-
-
1
module Uploader
-
# Implements a different name pattern for versions than CarrierWave's
-
# default: we expect the version name at the end of the filename separated
-
# by a dash
-
1
module Versions
-
1
private
-
-
# put version at the end of the filename
-
1
def full_filename for_file
-
3867
name = super(for_file)
-
3867
parts = name.split "."
-
3867
basename = [parts.shift, version_name].compact.join("-")
-
3867
"#{basename}.#{parts.join('.')}"
-
end
-
end
-
end
-
-
# Takes care of the file upload for cards with attached files.
-
# Most of the upload behaviour depends on the card itself.
-
# (e.g. card type and storage option chosen for the card). So in contrary
-
# to CarrierWave's default uploader we depend very much on the model
-
# (= card object) to get the correct paths for retrieving and storing
-
# the file.
-
#
-
# Cards that support attachments (by default those are cards of type "file"
-
# and "image") accept a file handle as a card attribute.
-
#
-
# @example Attaching a file to a file card
-
# Card.create name: "file card", type: :file,
-
# file: File.new(path_to_file)
-
#
-
# @example Attaching a image to a image card
-
# Card.create name: "file card", type: :image,
-
# image: File.new(path_to_image)
-
#
-
# It's possible to upload files using a url. The card attribute for that is
-
# remote_<attachment_type>_url
-
#
-
# @example Create a file card using a remote url
-
# Card.create name: "file_card", type: :file,
-
# remote_file_url: "http://a.file.in/the.web"
-
#
-
# @example Updating a image card using a remote url
-
# card.update remote_image_url: "http://a.image/somewhere.png"
-
#
-
# ## Storage types
-
# You can choose between four different storage options
-
# - coded: These files are in the codebase, like the default logo.
-
# Every view is a decko request.
-
# - local: Uploaded files which are stored in a local upload directory
-
# (upload path is configurable via config.paths["files"]).
-
# If read permissions are set such that "Anyone" can read, then there is
-
# a symlink from the public directory. Otherwise every view is a decko
-
# request.
-
# - cloud: You can configure buckets that refer to an external storage
-
# service. Link is rendered as absolute url
-
# - web: A fixed url (to external source). No upload or other file
-
# processing. Link is just the saved url.
-
#
-
# Currently, there is no web interface that let's a user or administrator
-
# choose a storage option for a specific card or set of cards.
-
# There is only a global config option to set the storage type for all new
-
# uploads (config.storage_type). On the *admin card it's possible to
-
# update all existing file cards according to the current global config.
-
#
-
# Storage types for single cards can be changed by developers using
-
# the card attributes "storage_type", "bucket", and "mod".
-
#
-
# @example Creating a hard-coded file
-
# Card.create name: "file card", type_id: Card::FileID,
-
# file: File.new(path),
-
# storage_type: :coded, mod: "account"
-
#
-
# @example Moving a file to a cloud service
-
# # my_deck/config/application.rb:
-
# config.file_buckets = {
-
# aws_bucket: {
-
# provider: "fog/aws",
-
# directory: "bucket-name",
-
# subdirectory: "files",
-
# credentials: {
-
# provider: 'AWS' # required
-
# aws_access_key_id: 'key' # required
-
# aws_secret_access_key: 'secret-key' # required
-
# public: true,
-
# }
-
# }
-
#
-
# # decko console or rake task:
-
# card.update storage_type: :cloud, bucket: :aws_bucket
-
#
-
# @example Creating a file card with fixed external link
-
# Card.create name: "file card", type_id: Card::FileID,
-
# content: "http://animals.org/cat.png"
-
# storage_type: :web
-
#
-
# Card.create name: "file card", type_id: Card::FileID,
-
# file: "http://animals.org/cat.png"
-
# storage_type: :web
-
#
-
# Depending on the storage type the uploader uses the following paths
-
# and identifiers.
-
# ### Identifier (stored in the database as db_content)
-
# - coded: :codename/mod_name.ext
-
# - local: ~card_id/action_id.ext
-
# - cloud: (bucket)/card_id/action_id.ext
-
# - web: http://url
-
#
-
# ### Storage path
-
# - coded:
-
# mod_dir/file/codename/type_code(-variant).ext (no colon on codename!)
-
# - local:
-
# files_dir/card_id/action_id(-variant).ext (no tilde on id!)
-
# - cloud:
-
# bucket/bucket_subdir/id/action_id(-variant).ext
-
# - web: no storage
-
#
-
# Variants are only used for images. Possible options are
-
# icon|small|medium|large|original.
-
# files_dir, bucket, and bucket_subdir can be changed via config options.
-
#
-
# ### Supported url patterns
-
# mark.ext
-
# mark/revision.ext
-
# mark/revision-variant.ext
-
# /files/mark/revision-variant.ext # <- public symlink if readable by
-
# # "Anyone"
-
#
-
# <mark> can be one of the following options
-
# - <card name>
-
# - ~<card id>
-
# - :<code name>
-
#
-
# <revision> is the mod name if the file is coded or and action_id in any
-
# case
-
#
-
# Examples:
-
# *logo.png
-
# ~22/33-medium.png # local
-
# :yeti_skin/standard-large.png # coded
-
#
-
1
class FileCardUploader < Uploader::Base
-
1
attr_accessor :mod
-
1
include Card::Env::Location
-
-
1
STORAGE_TYPES = [:cloud, :web, :coded, :local].freeze
-
1
CONFIG_OPTIONS = [:provider, :attributes, :directory, :public, :credentials,
-
:authenticated_url_expiration, :use_ssl_for_aws].freeze
-
1
CONFIG_CREDENTIAL_OPTIONS = [
-
:provider,
-
:aws_access_key_id, :aws_secret_access_key, :region, :host, :endpoint,
-
:google_access_key_id, :google_secret_access_key
-
].freeze
-
1
delegate :store_dir, :retrieve_dir, :file_dir, :mod, :bucket, to: :model
-
-
1
def valid?
-
262
extension.present?
-
end
-
-
1
def filename
-
2557
if model.coded?
-
2154
"#{model.type_code}#{extension}"
-
else
-
403
"#{action_id}#{extension}"
-
end
-
end
-
-
1
def extension
-
case
-
6397
when file&.extension.present? then ".#{file.extension}"
-
2335
when card_content = model.content then File.extname(card_content)
-
when orig = original_filename then File.extname(orig)
-
else ""
-
end.downcase
-
end
-
-
# generate identifier that gets stored in the card's db_content field
-
# @param opts [Hash] generate an identifier using the given storage options
-
# instead of the storage options derived from the model and
-
# the global configuration
-
# @option opts [Symbol] storage_type
-
# @option opts [String] mod
-
# @option opts [Symbol] bucket
-
1
def db_content opts={}
-
31
model.with_storage_options opts do
-
31
return model.content if model.web?
-
31
return "" unless file.present?
-
31
"%s/%s" % [file_dir, url_filename]
-
end
-
end
-
-
1
def url_filename opts={}
-
1226
model.with_storage_options opts do
-
1226
if model.coded?
-
1136
"#{model.mod}#{extension}"
-
else
-
90
"#{action_id}#{extension}"
-
end
-
end
-
end
-
-
# @option opts [Symbol] :absolute - return absolute url
-
1
def url opts={}
-
1195
if model.cloud?
-
file&.url
-
1195
elsif model.web?
-
model.content
-
else
-
1195
local_url opts
-
end
-
end
-
-
1
def local_url opts={}
-
1195
"%s/%s/%s" % [local_url_base(opts), file_dir, full_filename(url_filename(opts))]
-
end
-
-
1
def local_url_base opts={}
-
1195
web_path = Card.config.files_web_path
-
1195
opts.delete(:absolute) ? card_url(web_path) : card_path(web_path)
-
end
-
-
1
def public_path
-
37
File.join Cardio.paths["public"].existent.first, url
-
end
-
-
1
def cache_dir
-
183
@model.files_base_dir + "/cache"
-
end
-
-
# Carrierwave calls store_path without argument when it stores the file
-
# and with the identifier from the db when it retrieves the file.
-
# In our case the first part of our identifier is not part of the path
-
# but we can construct the filename from db data. So we don't need the
-
# identifier.
-
1
def store_path for_file=nil
-
2524
if for_file
-
2451
retrieve_path
-
else
-
73
File.join([store_dir, full_filename(filename)].compact)
-
end
-
end
-
-
1
def retrieve_path
-
2451
File.join([retrieve_dir, full_filename(filename)].compact)
-
end
-
-
1
def tmp_path
-
Dir.mkdir model.tmp_upload_dir unless Dir.exist? model.tmp_upload_dir
-
File.join model.tmp_upload_dir, filename
-
end
-
-
1
def create_versions? new_file
-
2356
model.create_versions? new_file
-
end
-
-
# paperclip compatibility used in type/file.rb#core (base format)
-
1
def path version=nil
-
51
version ? versions[version].path : super()
-
end
-
-
1
def original_filename
-
238
@original_filename ||= model.selected_action &&
-
model.selected_action.comment
-
end
-
-
1
def action_id
-
493
model.selected_content_action_id || action_id_stand_in
-
end
-
-
# delegate carrierwave's fog config methods to bucket configuration
-
1
::CarrierWave::FileCardUploader::CONFIG_OPTIONS.each do |name|
-
7
define_method("fog_#{name}") { bucket_config name }
-
end
-
-
1
def bucket_config option
-
@model.bucket_config[option]
-
end
-
-
1
def asset_host
-
bucket_config(:asset_host) || super
-
end
-
-
1
private
-
-
# used as action_id in the filename
-
# if card is not #actionable?
-
1
def action_id_stand_in
-
1
Time.now.to_i
-
end
-
-
1
def storage
-
2546
case @model.storage_type
-
when :cloud
-
::CarrierWave::Storage::Fog.new(self)
-
else
-
2546
::CarrierWave::Storage::File.new(self)
-
end
-
end
-
end
-
end
-
1
require "mini_magick"
-
-
1
module CarrierWave
-
# Adds image specific version handling to {FileCardUploader}.
-
# The ImageCardUploader creates five versions of different sizes when it
-
# uploads an imagae file:
-
# icon (16x16), small (75x75), medium (200X200), large (500x500) and
-
# the original size.
-
1
class ImageCardUploader < FileCardUploader
-
1
include CarrierWave::MiniMagick
-
-
1
def path version=nil
-
48
(version && version != :original) ? versions[version].path : super()
-
end
-
-
1
version :icon, if: :create_versions?, from_version: :small do
-
1
process resize_and_pad: [16, 16]
-
end
-
1
version :small, if: :create_versions?, from_version: :medium do
-
1
process resize_to_fit: [75, 75]
-
end
-
1
version :medium, if: :create_versions? do
-
1
process resize_to_limit: [200, 200]
-
end
-
1
version :large, if: :create_versions? do
-
1
process resize_to_limit: [500, 500]
-
end
-
-
# version :small_square, if: :create_versions?,
-
# from_version: :medium_square do
-
# process resize_to_fill: [75, 75]
-
# end
-
# version :medium_square, if: :create_versions? do
-
# process resize_to_fill: [200, 200]
-
# end
-
#
-
# In case we decide to support the squared versions
-
# we have to update all existing images with the following snippet:
-
# Card.search(type_id: Card::ImageID) do |card|
-
# card.image.cache_stored_file!
-
# card.image.recreate_versions!
-
# end
-
-
1
def identifier
-
38
full_filename(super())
-
end
-
-
# add 'original' if no version is given
-
1
def full_filename for_file
-
2279
name = super(for_file)
-
2279
if version_name
-
1669
name
-
else
-
610
parts = name.split "."
-
610
"#{parts.shift}-original.#{parts.join('.')}"
-
end
-
end
-
end
-
end
-
1
require "delayed_job_active_record"
-
1
module Cardio
-
# override default methods to handle DelayedJob needs
-
1
module DelayedJob
-
1
def delaying! on=true
-
18
super
-
18
Delayed::Worker.delay_jobs = Cardio.config.delaying
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
1
class Format
-
# Format text for use in html email messages
-
1
class EmailHtmlFormat < Card::Format::HtmlFormat
-
1
@@aliases["email"] = "email_html"
-
-
1
def self.view_caching?
-
false
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
1
class Format
-
# Format text for use in plain text email messages
-
1
class EmailTextFormat < Card::Format::TextFormat
-
1
def chunk_list
-
48
:references
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
# module to be included in cards used as options for follow rules
-
1
module FollowOption
-
# Hash containing an applicability test for each option (block)
-
1
@test = {}
-
# Hash containing an id-list-generating block for each option
-
1
@follower_candidate_ids = {}
-
# Hash that registers / groups options
-
1
@options = { all: [], main: [], restrictive: [] }
-
-
1
class << self
-
1
attr_reader :test, :follower_candidate_ids, :options
-
-
1
def codenames type=:all
-
8
options[type]
-
end
-
-
1
def cards
-
codenames.map { |codename| Card[codename] }
-
end
-
-
1
def restrictive_options
-
codenames :restrictive
-
end
-
-
1
def main_options
-
codenames :main
-
end
-
end
-
end
-
end
-
1
class Card
-
# stash followers of a given card
-
1
class FollowerStash
-
1
def initialize card=nil
-
104
@stash = Hash.new { |h, v| h[v] = [] }
-
86
@checked = ::Set.new
-
86
check_card(card) if card
-
end
-
-
1
def check_card card
-
166
return if @checked.include? card.key
-
-
165
Auth.as_bot do
-
165
@checked.add card.key
-
165
stash_direct_followers card
-
165
stash_field_followers card.left
-
end
-
end
-
-
1
def followers
-
@stash.keys
-
end
-
-
1
def each_follower_with_reason
-
# "follower"(=user) is a card object, "followed"(=reasons) a card name
-
86
@stash.each do |follower_card, reasons|
-
18
yield(follower_card, reasons.first)
-
end
-
end
-
-
1
private
-
-
1
def stash_direct_followers card
-
165
card.each_direct_follower_id_with_reason do |user_id, reason|
-
20
stash Card.fetch(user_id), reason
-
end
-
end
-
-
1
def stash_field_followers card
-
165
return unless (fields = follow_fields card)
-
-
88
fields.each do |field|
-
88
break if stash_field_follower card, field
-
end
-
end
-
-
1
def stash_field_follower card, field
-
88
return false unless checked?(field.to_name) || nested?(card, field)
-
-
11
check_card card
-
11
true
-
end
-
-
1
def nested? card, field
-
88
return unless field.to_name.key == includes_card_key
-
-
88
@checked.intersection(nestee_set(card)).any?
-
end
-
-
1
def includes_card_key
-
88
@includes_card_key ||= :nests.cardname.key
-
end
-
-
1
def nestee_set card
-
88
@nestee_set ||= {}
-
88
@nestee_set[card.key] ||= nestee_search card
-
end
-
-
1
def nestee_search card
-
88
Card.search({ return: "key", included_by: card.name },
-
"follow cards included by #{card.name}")
-
end
-
-
1
def checked? name
-
201
@checked.include? name.key
-
end
-
-
1
def follow_fields card
-
165
return unless card && !checked?(card.name)
-
-
88
card.rule_card(:follow_fields)&.item_names(context: card.name)
-
end
-
-
1
def stash follower, reason
-
20
@stash[follower] << reason
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
1
class Format
-
1
class CssFormat < Format
-
1
register :css
-
-
1
def mime_type
-
"text/css"
-
end
-
-
1
def self.view_caching?
-
false
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
1
class Format
-
1
class CsvFormat < TextFormat
-
1
register :csv
-
-
1
def mime_type
-
"text/comma-separated-values"
-
end
-
-
1
def self.view_caching?
-
# TODO: make view caching handle non-strings
-
# (specifically stub_render)
-
false
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
1
class Format
-
1
class FileFormat < Format
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
1
class Format
-
1
class JsFormat < Format
-
1
register :js
-
-
1
def self.view_caching?
-
false
-
end
-
-
1
def mime_type
-
"text/javascript"
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
1
class Format
-
1
class JsonFormat < DataFormat
-
1
register :json
-
-
1
def mime_type
-
11
"text/json"
-
end
-
-
1
def expand_stubs content
-
case content
-
when Hash
-
content.each { |k, v| content[k] = expand_stubs v }
-
when Array
-
content.map { |item| expand_stubs item }
-
else
-
super
-
end
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
1
class Format
-
1
class RssFormat < HtmlFormat
-
1
register :rss
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
1
class Format
-
1
class XmlFormat < DataFormat
-
1
register :xml
-
-
1
def mime_type
-
"text/xml"
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
# An "act" is a group of recorded {Card::Action actions} on {Card cards}.
-
# Together, {Act acts}, {Action actions}, and {Change changes} comprise a
-
# comprehensive {Card card} history tracking system.
-
#
-
# For example, if a given web form submissions updates the contents of three cards,
-
# then the submission will result in the recording of three {Action actions}, each
-
# of which is tied to one {Act act}.
-
#
-
# Each act records:
-
#
-
# - the _actor_id_ (an id associated with the account responsible)
-
# - the _card_id_ of the act's primary card
-
# - _acted_at_, a timestamp of the action
-
# - the _ip_address_ of the actor where applicable.
-
#
-
1
class Act < ApplicationRecord
-
1
before_save :assign_actor
-
307
has_many :ar_actions, -> { order :id }, foreign_key: :card_act_id,
-
inverse_of: :act,
-
class_name: "Card::Action"
-
1
class << self
-
# remove all acts that have no card. (janitorial)
-
#
-
# CAREFUL - could still have actions even if act card is gone...
-
1
def delete_cardless
-
left_join = "LEFT JOIN cards ON card_acts.card_id = cards.id"
-
joins(left_join).where("cards.id IS NULL").delete_all
-
end
-
-
# remove all acts that have no action. (janitorial)
-
1
def delete_actionless
-
joins(
-
"LEFT JOIN card_actions ON card_acts.id = card_act_id"
-
).where(
-
"card_actions.id is null"
-
).delete_all
-
end
-
-
# all acts with actions on a given list of cards
-
# @param card_ids [Array of Integers]
-
# @param with_drafts [true, false] (only shows drafts of current user)
-
# @return [Array of Acts]
-
1
def all_with_actions_on card_ids, with_drafts=false
-
sql = "card_actions.card_id IN (:card_ids) AND (draft is not true"
-
sql << (with_drafts ? " OR actor_id = :user_id)" : ")")
-
all_viewable([sql, { card_ids: card_ids, user_id: Card::Auth.current_id }])
-
end
-
-
# all acts with actions that current user has permission to view
-
# @return [ActiveRecord Relation]
-
1
def all_viewable action_where=nil
-
relation = joins(ar_actions: :ar_card)
-
relation = relation.where(action_where) if action_where
-
relation.where(Query::CardQuery.viewable_sql).where.not(card_id: nil).distinct
-
end
-
-
1
def cache
-
115
Card::Cache[Card::Act]
-
end
-
-
# used by rails time_ago
-
# timestamp is set by rails on create
-
1
def timestamp_attributes_for_create
-
1
super << "acted_at"
-
end
-
end
-
-
1
def actor
-
22
Card.fetch actor_id
-
end
-
-
# the act's primary card
-
# @return [Card]
-
1
def card
-
36
Card.fetch card_id, look_in_trash: true # , skip_modules: true
-
-
# FIXME: if the following is necessary, we need to document why.
-
# generally it's a very bad idea to have type-specific code here.
-
-
# return res unless res&.type_id&.in?([Card::FileID, Card::ImageID])
-
# res.include_set_modules
-
end
-
-
# list of all actions that are part of the act
-
# @return [Array]
-
1
def actions cached=true
-
201
return ar_actions unless cached
-
-
128
self.class.cache.fetch("#{id}-actions") { ar_actions.find_all.to_a }
-
end
-
-
# act's action on the card in question
-
# @param card_id [Integer]
-
# @return [Card::Action]
-
1
def action_on card_id
-
53
actions.find do |action|
-
59
action.card_id == card_id && !action.draft
-
end
-
end
-
-
# act's action on primary card if it exists. otherwise act's first action
-
# @return [Card::Action]
-
1
def main_action
-
53
action_on(card_id) || actions.first
-
end
-
-
1
def draft?
-
main_action.draft
-
end
-
-
# time (in words) since act took place
-
# @return [String]
-
1
def elapsed_time
-
DateTime.new(acted_at).distance_of_time_in_words_to_now
-
end
-
-
# act's actions on either the card itself or another card that includes it
-
# @param card [Card]
-
# @return [Array of Actions]
-
1
def actions_affecting card
-
51
actions.select do |action|
-
81
(card.id == action.card_id) ||
-
card.nestee_ids.include?(action.card_id)
-
end
-
end
-
-
1
private
-
-
# used by before filter
-
1
def assign_actor
-
267
self.actor_id ||= Auth.current_id
-
end
-
end
-
end
-
1
class Card
-
1
class Act
-
1
class ActRenderer
-
1
def initialize format, act, args
-
2
@format = format
-
2
@act = act
-
2
@act_card = act.card
-
2
@args = args
-
2
@card = @format.card
-
2
@context = @args[:act_context]
-
end
-
-
1
include ::Bootstrapper
-
-
1
def method_missing method_name, *args, &block
-
44
if block_given?
-
8
@format.send(method_name, *args, &block)
-
else
-
36
@format.send(method_name, *args)
-
end
-
end
-
-
1
def respond_to_missing? method_name, _include_private=false
-
@format.respond_to? method_name
-
end
-
-
1
def render
-
2
return "" unless @act_card
-
-
2
act_accordion
-
end
-
-
1
def header
-
#::Bootstrap.new(self).render do
-
2
bs_layout do
-
2
row xs: [10, 2] do
-
2
column do
-
2
html title
-
4
tag(:span, "text-muted pl-1 badge") { summary }
-
end
-
2
column act_links, class: "text-right"
-
end
-
end
-
# end
-
end
-
-
1
def absolute_title
-
2
accordion_expand_link(@act_card.name)
-
end
-
-
1
def details
-
2
approved_actions[0..20].map do |action|
-
2
Action::ActionRenderer.new(@format, action, action_header?,
-
:summary).render
-
end.join
-
end
-
-
1
def summary
-
2
%i[create update delete draft].map do |type|
-
8
next unless count_types[type].positive?
-
-
2
"#{@format.action_icon type}<small> #{count_types[type]}</small>"
-
end.compact.join "<small class='text-muted'> | </small>"
-
end
-
-
1
def act_links
-
[
-
2
link_to_history,
-
2
(link_to_act_card unless @act_card.trash)
-
].compact.join " "
-
end
-
-
1
def link_to_act_card
-
2
link_to_card @act_card, icon_tag(:new_window), class: "_stop_propagation"
-
end
-
-
1
def link_to_history
-
2
link_to_card @act_card, icon_tag(:history),
-
path: { view: :history, look_in_trash: true },
-
class: "_stop_propagation",
-
rel: "nofollow"
-
end
-
-
1
def approved_actions
-
6
@approved_actions ||= actions.select { |a| a.card&.ok?(:read) }
-
# FIXME: should not need to test for presence of card here.
-
end
-
-
1
def action_header?
-
2
true
-
# @action_header ||= approved_actions.size != 1 ||
-
# approved_actions[0].card_id != @format.card.id
-
end
-
-
1
def count_types
-
10
@count_types ||=
-
approved_actions.each_with_object(
-
8
Hash.new { |h, k| h[k] = 0 }
-
) do |action, type_cnt|
-
2
type_cnt[action.action_type] += 1
-
end
-
end
-
-
1
def edited_ago
-
2
return "" unless @act.acted_at
-
-
2
"#{time_ago_in_words(@act.acted_at)} ago"
-
end
-
-
1
def collapse_id
-
12
"act-id-#{@act.id}"
-
end
-
-
1
def accordion_expand_link text
-
2
<<-HTML
-
<a>
-
#{text}
-
</a>
-
HTML
-
end
-
-
# TODO: change accordion API in bootstrap/helper.rb so that it can be used
-
# here. The problem is that here we have extra links in the title
-
# that are not supposed to expand the accordion
-
1
def act_accordion
-
2
context = @act.main_action.draft ? :warning : :default
-
2
<<-HTML
-
<div class="card card-#{context} nodblclick">
-
#{act_accordion_panel}
-
</div>
-
HTML
-
end
-
-
1
def accordion_expand_options
-
{
-
2
"data-toggle" => "collapse",
-
"data-target" => ".#{collapse_id}",
-
"aria-expanded" => true,
-
"aria-controls" => collapse_id
-
}
-
end
-
-
1
def act_panel_options
-
2
{ class: "card-header", role: "tab", id: "heading-#{collapse_id}" }
-
end
-
-
1
def act_accordion_panel
-
2
act_accordion_heading + act_accordion_body
-
end
-
-
1
def act_accordion_heading
-
2
wrap_with :div, act_panel_options.merge(accordion_expand_options) do
-
2
wrap_with(:h5, header, class: "mb-0") + subtitle
-
end
-
end
-
-
1
def act_accordion_body
-
2
wrap_with :div, id: collapse_id,
-
class: "collapse #{collapse_id}",
-
"data-parent": ".act-accordion-group" do
-
2
wrap_with :div, details, class: "card-body"
-
end
-
end
-
-
# Revert:
-
# current update
-
# Restore:
-
# current deletion
-
# Revert and Restore:
-
# old deletions
-
# blank:
-
# current create
-
# save as current:
-
# not current, not deletion
-
1
def rollback_link
-
2
return unless card.ok? :update
-
-
2
wrap_with :div, class: "act-link collapse #{collapse_id} float-right" do
-
2
content_tag(:small, revert_link)
-
-
# link_to "Save as current",
-
# class: "slotter", remote: true,
-
# method: :post, rel: "nofollow",
-
# "data-slot-selector" => ".card-slot.history-view",
-
# path: { action: :update, action_ids: prior,
-
# view: :open, look_in_trash: true }
-
end
-
end
-
-
1
def deletion_act?
-
act_type == :delete
-
end
-
-
1
def act_type
-
@act.main_action.action_type
-
end
-
-
1
def show_or_hide_changes_link
-
wrap_with :div, class: "act-link" do
-
@format.link_to_view(
-
:act, "#{@args[:hide_diff] ? 'Show' : 'Hide'} changes",
-
path: { act_id: @args[:act].id, act_seq: @args[:act_seq],
-
hide_diff: !@args[:hide_diff], action_view: :expanded,
-
act_context: @args[:act_context], look_in_trash: true }
-
)
-
end
-
end
-
-
1
def autosaved_draft_link opts={}
-
text = opts.delete(:text) || "autosaved draft"
-
opts[:path] = { edit_draft: true }
-
add_class opts, "navbar-link"
-
link_to_view :edit, text, opts
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Act
-
1
class ActRenderer
-
# Used for recent changes.
-
# It shows all actions of an act
-
1
class AbsoluteActRenderer < ActRenderer
-
1
def title
-
2
absolute_title
-
end
-
-
1
def subtitle
-
2
wrap_with :small do
-
[
-
2
@format.link_to_card(@act.actor, nil, class: "_stop_propagation"),
-
edited_ago,
-
rollback_link
-
]
-
end
-
end
-
-
# FIXME: how do we know we need main here??
-
1
def revert_link
-
2
revert_actions_link "revert to previous",
-
{ revert_to: :previous, revert_act: @act.id },
-
"data-slot-selector": "#main > .card-slot"
-
end
-
-
1
def actions
-
2
@act.actions
-
end
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
# An _action_ is a group of {Card::Change changes} to a single {Card card}
-
# that is recorded during an {Card::Act act}.
-
# Together, {Act acts}, {Action actions}, and {Change changes} comprise a
-
# comprehensive {Card card} history tracking system.
-
#
-
# For example, if a given web submission changes both the name and type of
-
# a given card, that would be recorded as one {Action action} with two
-
# {Change changes}. If there are multiple cards changed, each card would
-
# have its own {Action action}, but the whole submission would still comprise
-
# just one single {Act act}.
-
#
-
# An {Action} records:
-
#
-
# * the _card_id_ of the {Card card} acted upon
-
# * the _card_act_id_ of the {Card::Act act} of which the action is part
-
# * the _action_type_ (create, update, or delete)
-
# * a boolean indicated whether the action is a _draft_
-
# * a _comment_ (where applicable)
-
#
-
1
class Action < ApplicationRecord
-
1
include Differ
-
1
extend Admin
-
-
1
belongs_to :act, foreign_key: :card_act_id, inverse_of: :ar_actions
-
1
belongs_to :ar_card, foreign_key: :card_id, inverse_of: :actions, class_name: "Card"
-
1
has_many :card_changes, foreign_key: :card_action_id,
-
inverse_of: :action,
-
dependent: :delete_all,
-
class_name: "Card::Change"
-
1
belongs_to :super_action, class_name: "Action", inverse_of: :sub_actions
-
1
has_many :sub_actions, class_name: "Action", inverse_of: :super_action
-
-
1
scope :created_by, lambda { |actor_id|
-
210
joins(:act).where "card_acts.actor_id = ?", actor_id
-
}
-
-
# these are the three possible values for action_type
-
1
TYPE_OPTIONS = %i[create update delete].freeze
-
-
1
after_save :expire
-
-
1
class << self
-
# retrieve action from cache if available
-
# @param id [id of Action]
-
# @return [Action, nil]
-
1
def fetch id
-
74
cache.fetch id.to_s do
-
18
find id.to_i
-
end
-
end
-
-
# cache object for actions
-
# @return [Card::Cache]
-
1
def cache
-
736
Card::Cache[Action]
-
end
-
-
1
def all_with_cards
-
joins :ar_card
-
end
-
-
1
def all_viewable
-
all_with_cards.where Query::CardQuery.viewable_sql
-
end
-
end
-
-
# each action is associated with on and only one card
-
# @return [Card]
-
1
def card
-
698
Card.fetch card_id, look_in_trash: true
-
-
# I'm not sure what the rationale for the following was/is, but it was causing
-
# problems in cases where slot attributes are overridden (eg see #wrap_data in
-
# sources on wikirate). The problem is the format object had the set modules but
-
# the card didn't.
-
#
-
# My guess is that the need for the following had something to do with errors
-
# associated with changed types. If so, the solution probably needs to handle
-
# including the set modules associated with the type at the time of the action
-
# rather than including no set modules at all.
-
#
-
# What's more, we _definitely_ don't want to hard code special behavior for
-
# specific types in here!
-
-
# , skip_modules: true
-
# return res unless res && res.type_id.in?([Card::FileID, Card::ImageID])
-
# res.include_set_modules
-
end
-
-
# remove action from action cache
-
1
def expire
-
546
self.class.cache.delete id.to_s
-
end
-
-
# assign action_type (create, update, or delete)
-
# @param value [Symbol]
-
# @return [Integer]
-
1
def action_type= value
-
362
write_attribute :action_type, TYPE_OPTIONS.index(value)
-
end
-
-
# retrieve action_type (create, update, or delete)
-
# @return [Symbol]
-
1
def action_type
-
615
return :draft if draft
-
-
615
TYPE_OPTIONS[read_attribute(:action_type)]
-
end
-
-
1
def previous_action
-
Card::Action.where("id < ? AND card_id = ?", id, card_id).last
-
end
-
-
# value set by action's {Change} to given field
-
# @see #interpret_field #interpret_field for field param
-
# @see #interpret_value #interpret_value for return values
-
1
def value field
-
279
return unless (change = change field)
-
-
141
interpret_value field, change.value
-
end
-
-
# value of field set by most recent {Change} before this one
-
# @see #interpret_field #interpret_field for field param
-
# @see #interpret_field #interpret_field for field param
-
1
def previous_value field
-
8
return if action_type == :create
-
4
return unless (previous_change = previous_change field)
-
-
4
interpret_value field, previous_change.value
-
end
-
-
# action's {Change} object for given field
-
# @see #interpret_field #interpret_field for field param
-
# @return [Change]
-
1
def change field
-
279
changes[interpret_field field]
-
end
-
-
# most recent change to given field before this one
-
# @see #interpret_field #interpret_field for field param
-
# @return [Change]
-
1
def previous_change field
-
4
return nil if action_type == :create
-
-
4
field = interpret_field field
-
4
if @previous_changes&.key?(field)
-
@previous_changes[field]
-
else
-
4
@previous_changes ||= {}
-
4
@previous_changes[field] = card.last_change_on field, before: self
-
end
-
end
-
-
1
def all_changes
-
89
self.class.cache.fetch("#{id}-changes") do
-
# using card_changes causes caching problem
-
30
Card::Change.where(card_action_id: id).to_a
-
end
-
end
-
-
# all action {Change changes} in hash form. { field1: Change1 }
-
# @return [Hash]
-
1
def changes
-
279
@changes ||=
-
57
if sole?
-
25
current_changes
-
else
-
32
all_changes.each_with_object({}) do |change, hash|
-
25
hash[change.field.to_sym] = change
-
end
-
end
-
end
-
-
# all changed values in hash form. { field1: new_value }
-
1
def changed_values
-
@changed_values ||= changes.each_with_object({}) do |(key, change), h|
-
h[key] = change.value
-
end
-
end
-
-
# @return [Hash]
-
1
def current_changes
-
25
return {} unless card
-
-
25
@current_changes ||=
-
Card::Change::TRACKED_FIELDS.each_with_object({}) do |field, hash|
-
150
hash[field.to_sym] = Card::Change.new field: field,
-
value: card.send(field),
-
card_action_id: id
-
end
-
end
-
-
# translate field into fieldname as referred to in database
-
# @see Change::TRACKED_FIELDS
-
# @param field [Symbol] can be :type_id, :cardtype, :db_content, :content,
-
# :name, :trash
-
# @return [Symbol]
-
1
def interpret_field field
-
283
case field
-
50
when :content then :db_content
-
46
when :cardtype then :type_id
-
187
else field.to_sym
-
end
-
end
-
-
# value in form prescribed for specific field name
-
# @param value [value of {Change}]
-
# @return [Integer] for :type_id
-
# @return [String] for :name, :db_content, :content, :cardtype
-
# @return [True/False] for :trash
-
1
def interpret_value field, value
-
145
case field.to_sym
-
when :type_id
-
value&.to_i
-
when :cardtype
-
22
Card.fetch_name(value&.to_i)
-
123
else value
-
end
-
end
-
-
1
def sole?
-
57
all_changes.empty? &&
-
32
(action_type == :create || Card::Action.where(card_id: card_id).count == 1)
-
end
-
end
-
end
-
1
class Card
-
1
class Action
-
1
class ActionRenderer
-
1
attr_reader :action, :header
-
1
def initialize format, action, header=true, action_view=:summary, hide_diff=false
-
2
@format = format
-
2
@action = action
-
2
@header = header
-
2
@action_view = action_view
-
2
@hide_diff = hide_diff
-
end
-
-
1
include ::Bootstrapper
-
1
def method_missing method_name, *args, &block
-
22
if block_given?
-
@format.send(method_name, *args, &block)
-
else
-
22
@format.send(method_name, *args)
-
end
-
end
-
-
1
def respond_to_missing? method_name, _include_private=false
-
@format.respond_to? method_name
-
end
-
-
1
def render
-
2
classes = @format.classy("action-list")
-
2
bs_layout container: true, fluid: true do
-
2
row do
-
2
html <<-HTML
-
<ul class="#{classes} w-100">
-
<li class="#{action.action_type}">
-
#{action_panel}
-
</li>
-
</ul>
-
HTML
-
end
-
end
-
end
-
-
1
def action_panel
-
2
bs_panel do
-
2
if header
-
2
heading do
-
2
div type_diff, class: "float-right"
-
2
div name_diff
-
end
-
end
-
2
body do
-
2
content_diff
-
end
-
end
-
end
-
-
1
def name_diff
-
2
if @action.card == @format.card
-
2
name_changes
-
else
-
link_to_view(
-
:related, name_changes,
-
path: { slot: { items: { view: "history", nest_name: @action.card.name } } },
-
# "data-slot-selector" => ".card-slot.history-view"
-
)
-
end
-
end
-
-
1
def content_diff
-
2
return @action.raw_view if @action.action_type == :delete
-
-
2
@format.subformat(@action.card).render_action_summary action_id: @action.id
-
end
-
-
1
def type_diff
-
2
return "" unless @action.new_type?
-
-
@hide_diff ? @action.value(:cardtype) : @action.cardtype_diff
-
end
-
-
1
def name_changes
-
2
return old_name unless @action.new_name?
-
-
@hide_diff ? new_name : Card::Content::Diff.complete(old_name, new_name)
-
end
-
-
1
def old_name
-
2
(name = @action.previous_value :name) && title_in_context(name)
-
end
-
-
1
def new_name
-
title_in_context @action.value(:name)
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Action
-
# methods for administering card actions
-
1
module Admin
-
# permanently delete all {Action actions} not associated with a {Card}
-
1
def delete_cardless
-
left_join = "LEFT JOIN cards ON card_actions.card_id = cards.id"
-
joins(left_join).where("cards.id IS NULL").delete_all
-
end
-
-
# permanently delete all {Action actions} associate with non-current
-
# {Change changes}
-
1
def delete_old
-
Card::Change.delete_all
-
Card.find_each(&:delete_old_actions)
-
Card::Act.delete_actionless
-
end
-
-
# If an act is given then all remaining actions will be attached to that act.
-
# Otherwise the actions keep their acts.
-
1
def make_current_state_the_initial_state act=nil
-
Card::Change.delete_all
-
Card.find_each(&:delete_old_actions)
-
action_update = { action_type: Card::Action::TYPE_OPTIONS.index(:create) }
-
action_update[:card_act_id] = act.id if act
-
Card::Action.update_all action_update
-
-
if act
-
Card::Act.where("id != :id", id: act.id).delete_all
-
else
-
Card::Act.delete_actionless
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Action
-
# a collection of methods for comparing actions
-
1
module Differ
-
# compare action's name value with previous name value
-
# @return [rendered diff]
-
1
def name_diff opts={}
-
return unless new_name?
-
-
diff_object(:name, opts).complete
-
end
-
-
# does action change card's name?
-
# @return [true/false]
-
1
def new_name?
-
2
!value(:name).nil?
-
end
-
-
# @return [rendered diff]
-
# compare action's cardtype value with previous cardtype value
-
1
def cardtype_diff opts={}
-
return unless new_type?
-
-
diff_object(:cardtype, opts).complete
-
end
-
-
# does action change card's type?
-
# @return [true/false]
-
1
def new_type?
-
2
!value(:type_id).nil?
-
end
-
-
# @return [rendered diff]
-
# compare action's content value with previous content value
-
1
def content_diff diff_type=:expanded, opts=nil
-
2
return unless new_content?
-
-
2
dobj = content_diff_object(opts)
-
2
diff_type == :summary ? dobj.summary : dobj.complete
-
end
-
-
# does action change card's content?
-
# @return [true/false]
-
1
def new_content?
-
141
!value(:db_content).nil?
-
end
-
-
# test whether content was visibly removed
-
# @return [true/false]
-
1
def red?
-
content_diff_object.red?
-
end
-
-
# test whether content was visibly added
-
# @return [true/false]
-
1
def green?
-
content_diff_object.green?
-
end
-
-
1
def raw_view content=nil
-
4
original_content = card.db_content
-
4
card.db_content = content || value(:db_content)
-
4
card.format.render_raw
-
ensure
-
4
card.db_content = original_content
-
end
-
-
1
def summary_diff_omits_content?
-
2
content_diff_object.summary_omits_content?
-
end
-
-
1
private
-
-
1
def diff_object field, opts
-
Card::Content::Diff.new previous_value(field), value(field), opts
-
end
-
-
1
def content_diff_object opts=nil
-
4
@diff ||= begin
-
2
diff_args = opts || card.include_set_modules.diff_args
-
2
previous_value = previous_value(:content)
-
2
previous = previous_value ? raw_view(previous_value) : ""
-
2
current = raw_view
-
2
Card::Content::Diff.new previous, current, diff_args
-
end
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
1
require 'activerecord-import'
-
-
1
class Card
-
# A _change_ is an alteration to a card's name, type, content, or trash state.
-
# Together, {Act acts}, {Action actions}, and {Change changes} comprise a
-
# comprehensive {Card card} history tracking system.
-
#
-
# For example, if a given web submission changes both the name and type of
-
# card, that would be recorded as one {Action action} with two
-
# {Change changes}.
-
#
-
# A {Change} records:
-
#
-
# * the _field_ changed
-
# * the new _value_ of that field
-
# * the {Action action} of which the change is part
-
#
-
1
class Change < ApplicationRecord
-
1
belongs_to :action, foreign_key: :card_action_id,
-
inverse_of: :card_changes
-
-
# lists the database fields for which changes are recorded
-
1
TRACKED_FIELDS = %w[name type_id db_content trash left_id right_id].freeze
-
-
1
class << self
-
# delete all {Change changes} not associated with an {Action action}
-
# (janitorial)
-
1
def delete_actionless
-
joins(
-
"LEFT JOIN card_actions "\
-
"ON card_changes.card_action_id = card_actions.id "
-
).where(
-
"card_actions.id is null"
-
).pluck_in_batches(:id) do |group_ids|
-
# used to be .delete_all here, but that was failing on large dbs
-
Rails.logger.info "deleting batch of changes"
-
where("id in (#{group_ids.join ','})").delete_all
-
end
-
end
-
-
# Change fields are recorded as integers. #field_index looks up the
-
# integer associated with a given field name.
-
# @param value [String, Symbol]
-
# @return [Integer]
-
1
def field_index value
-
26
value.is_a?(Integer) ? value : TRACKED_FIELDS.index(value.to_s)
-
end
-
-
# look up changes based on field name
-
# @param value [String, Symbol]
-
# @return [Change]
-
1
def find_by_field_name value
-
find_by_field field_index(value)
-
end
-
end
-
-
# set field value (integer)
-
# @param value [String, Symbol]
-
1
def field= value
-
361
write_attribute(:field, TRACKED_FIELDS.index(value.to_s))
-
end
-
-
# retrieve field name
-
# @return [String]
-
1
def field
-
25
TRACKED_FIELDS[read_attribute(:field)]
-
end
-
end
-
end
-
1
class Card
-
1
module Machine
-
1
REFRESHED = "MACHINE_ASSETS_REFRESHED".freeze
-
-
1
class << self
-
1
def refresh_script_and_style
-
414
return unless refresh_script_and_style?
-
Card.fetch(:all, :script)&.update_if_source_file_changed
-
Card.fetch(:all, :style)&.update_if_source_file_changed
-
end
-
-
1
private
-
-
1
def refresh_script_and_style?
-
414
case Cardio.config.machine_refresh
-
when :eager then true
-
when :cautious then cautious_refresh?
-
414
when :never then false
-
else
-
raise Card::Error,
-
"unknown option for machine_refresh: #{Cardio.config.machine_refresh}"
-
end
-
end
-
-
# only refresh when cache was cleared
-
1
def cautious_refresh?
-
return false unless Card::Cache.persistent_cache
-
return false if Card.cache.read REFRESHED
-
Card.cache.write REFRESHED, true
-
end
-
end
-
end
-
end
-
1
class ApplicationJob < ActiveJob::Base
-
end
-
1
class ApplicationRecord < ActiveRecord::Base
-
1
self.abstract_class = true
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
ActiveSupport.run_load_hooks(:before_card, self)
-
-
# Cards are wiki-inspired building blocks.
-
#
-
# This documentation is for developers who want to understand:
-
#
-
# 1. how ruby Card objects work, and
-
# 2. how to extend them.
-
#
-
# It assumes that you've already read the introductory text in {file:README.md}.
-
#
-
# Throughout this document we will refer to @card as an instance of a Card object.
-
#
-
# ## Names
-
#
-
# There are four important card identifiers, sometimes called "marks". Every card has a
-
# unique _name_, _key_, and _id_. Some cards also have a _codename_.
-
#
-
# @card.name # The name, a Card::Name object, is the most recognizable card
-
# mark.
-
# @card.key # The key, a String, is a simple lower-case name variant.
-
# @card.id # The id is an Integer.
-
# @card.codename # The codename, a Symbol, is the name by which a card can be
-
# referred to in code.
-
#
-
# All names with the same key (including the key itself) are considered variants of each
-
# other. No two cards can have names with the same key. {Card::Name} objects inherit from
-
# Strings but add many other methods for common card name patterns, eg
-
# `"A+B".to_name.right => "B"`.
-
#
-
# Setting a card's name, eg `@card.name = "New Name"`, will automatically update the key.
-
#
-
# - {Card::Name More on names.}
-
# - {Card::Codename More on codenames.}
-
#
-
# ## Type
-
#
-
# Every card has a type, and every type itself has an associated card. For example,
-
# _Paula_'s type might be _User_, so there is also a _User_ card.
-
#
-
# The type may be accessed in several ways:
-
#
-
# @card.type_id # returns id of type card [Integer]
-
# @card.type_name # returns name of type card [Card::Name]
-
# @card.type_code # returns codename of type card [Symbol]
-
# @card.type_card # returns Cardtype card associated with @card's type [Card]
-
#
-
# {file:mod/core/set/all/type.rb Common type methods}
-
#
-
# ## Content
-
#
-
# There are two primary methods for accessing a card's content:
-
#
-
# @card.db_content # the content as it appears in the database
-
# @card.content # the "official" content, which may be different from
-
# db_content when db_content is overridden with a structure rule.
-
#
-
# {Card::Content Processing card content}
-
#
-
# {file:mod/core/set/all/content.rb Common content methods}
-
#
-
# ## Fetch
-
#
-
# The two main ways to retrieve cards are fetching (retrieving cards one at a time) and
-
# querying (retrieving lists of cards). More on querying below.
-
#
-
# Any of the above marks (name, key, id, codename) can be used to fetch a card, eg:
-
#
-
# @card = Card.fetch "Garden" # returns the card with the name "Garden" (or, more
-
# precisely, with the key "garden")
-
# @card = Card.fetch 100 # returns the card with the id 100
-
# @card = Card.fetch :help # returns the card with the codename help
-
#
-
# The fetch API will first try to find the card in the cache and will only look in the
-
# database if necessary.
-
#
-
# {file:mod/core/set/all/fetch.rb More on fetching.}
-
#
-
# ## Query
-
#
-
# Card queries find and return lists of cards, eg:
-
#
-
# Card.search type_id: 4 # returns an Array of cards with the type_id of 4.
-
#
-
# {Card::Query More on queries}
-
#
-
# ## Views and Events
-
#
-
# Views and events are a _Shark's_ primary tools for manipulating cards. Views customize
-
# card presentation, while events customize card transactions. Or, if you like, views
-
# and events respectively alter cards in _space_ and _time_.
-
#
-
# Both views and events are defined in {Card::Mod mods}, short for modules or
-
# modifications.
-
#
-
# {Card::Set::Format::AbstractFormat More on views}
-
#
-
# {Card::Set::Event::Api More on events}
-
#
-
# ## Accounts and Permissions
-
#
-
# Card code is always executed in the context of a given user account. Permissions for
-
# that account are automatically checked when running a query, performing an action, or
-
# rendering a view. A typical query, for example, can only return cards that the current
-
# user has permission to read.
-
#
-
# You can see the current user with `Card::Auth.current`. The permissions of a proxy user
-
# can be temporarily assumed using `Card::Auth#as`.
-
#
-
# {Card::Auth More on accounts}
-
1
class Card < ApplicationRecord
-
1
extend Mark
-
1
extend Dirty::MethodFactory
-
1
include Dirty
-
1
include DirtyNames
-
1
include Director::CardMethods
-
-
1
Card::Cache # trigger autoload
-
-
1
has_many :references_in, class_name: :Reference, foreign_key: :referee_id
-
1
has_many :references_out, class_name: :Reference, foreign_key: :referer_id
-
23
has_many :acts, -> { order :id }
-
204
has_many :actions, -> { where(draft: [nil, false]).order :id }
-
421
has_many :drafts, -> { where(draft: true).order :id }, class_name: :Action
-
-
1
cattr_accessor :set_patterns, :action_specific_attributes, :set_specific_attributes
-
-
1
self.set_patterns = []
-
1
self.action_specific_attributes = [
-
:supercard,
-
:superleft,
-
:action,
-
:current_action,
-
:last_action_id_before_edit,
-
-
:skip, # skip event(s) for all cards in act
-
:skip_in_action, # skip event for just this card
-
:trigger, # trigger event(s) for all cards in act
-
:trigger_in_action, # trigger event for just this card
-
-
:comment, # obviated soon
-
-
# TODO: refactor following to use skip/trigger
-
:update_referers, # wrong mechanism for this
-
:update_all_users, # if the above is wrong then this one too
-
:silent_change # and this probably too
-
]
-
-
1
attr_accessor(*action_specific_attributes)
-
-
1
define_callbacks :select_action, :show_page, :act
-
-
1
ActiveSupport.run_load_hooks :card, self
-
end
-
1
ActiveSupport.run_load_hooks :after_card, self
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
# Singleton methods for account authentication and contextualization.
-
#
-
# Manages current user,
-
# "as" user, and password verification.
-
1
module Auth
-
1
extend Permissions
-
1
extend Proxy
-
1
extend Setup
-
1
extend Current
-
-
1
@as_card = @as_id = @current_id = @current = nil
-
-
1
class << self
-
# authenticate a user by their login name and unencrypted password.
-
# @param email [String]
-
# @param password [String]
-
# @return [+*account card, nil]
-
1
def authenticate email, password
-
71
account = Auth.find_account_by_email email
-
case
-
71
when !account then nil
-
when !account.active? then nil
-
when Card.config.no_authentication then account
-
71
when password_valid?(account, password.strip) then account
-
end
-
end
-
-
# check whether password is correct for account card
-
# @param account [+*account card]
-
# @param password [String]
-
1
def password_valid? account, password
-
71
account.password == encrypt(password, account.salt)
-
end
-
-
# encrypt password string with the given salt.
-
# @return [SHA1 String]
-
1
def encrypt password, salt
-
74
Digest::SHA1.hexdigest "#{salt}--#{password}--"
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Auth
-
# methods for setting current account
-
1
module Current
-
# set current user in process and session
-
1
def signin cardish
-
524
signin_id = Card.id(cardish) || Card::AnonymousID
-
524
self.current_id = signin_id
-
524
set_session_user signin_id
-
end
-
-
# current user is not anonymous
-
# @return [true/false]
-
1
def signed_in?
-
1482
current_id != Card::AnonymousID
-
end
-
-
# id of current user card.
-
# @return [Integer]
-
1
def current_id
-
18870
@current_id ||= Card::AnonymousID
-
end
-
-
# current accounted card (must have +\*account)
-
# @return [Card]
-
1
def current
-
922
if @current && @current.id == current_id
-
704
@current
-
else
-
218
@current = Card[current_id]
-
end
-
end
-
-
1
def current_roles
-
784
@current_roles ||= [Card.fetch_name(:anyone_signed_in),
-
current.fetch(:roles)&.item_names].flatten.compact
-
end
-
-
1
def serialize
-
95
{ as_id: as_id, current_id: current_id }
-
end
-
-
# @param auth_data [Integer|Hash] user id, user name, or a hash
-
# @option auth_data [Integer] current_id
-
# @option auth_data [Integer] as_id
-
1
def with auth_data
-
41
if auth_data.is_a?(Integer) || auth_data.is_a?(String)
-
auth_data = { current_id: Card.id(auth_data) }
-
end
-
-
41
tmp_current_id = current_id
-
41
tmp_as_id = as_id
-
41
tmp_current = @current
-
41
tmp_as_card = @as_card
-
41
tmp_current_roles = @current_roles
-
-
# resets @as and @as_card
-
41
self.current_id = auth_data[:current_id]
-
41
@as_id = auth_data[:as_id] if auth_data[:as_id]
-
41
yield
-
ensure
-
41
@current_id = tmp_current_id
-
41
@as_id = tmp_as_id
-
41
@current = tmp_current
-
41
@as_card = tmp_as_card
-
41
@current_roles = tmp_current_roles
-
end
-
-
# get session object from Env
-
# return [Session]
-
1
def session
-
966
Card::Env.session
-
end
-
-
# set current from token, api_key, or session
-
1
def signin_with opts={}
-
444
if opts[:token]
-
2
signin_with_token opts[:token]
-
442
elsif opts[:api_key]
-
signin_with_api_key opts[:api_key]
-
else
-
442
signin_with_session
-
end
-
end
-
-
# set the current user based on token
-
1
def signin_with_token token
-
2
payload = Token.validate! token
-
2
signin payload[:anonymous] ? Card::AnonymousID : payload[:user_id]
-
end
-
-
# set the current user based on api_key
-
1
def signin_with_api_key api_key
-
account = find_account_by_api_key api_key
-
unless account&.validate_api_key! api_key
-
raise Card::Error::PermissionDenied, "API key authentication failed"
-
end
-
-
signin account.left_id
-
end
-
-
# get :user id from session and set Auth.current_id
-
1
def signin_with_session
-
442
card_id = session_user
-
442
signin(card_id && Card.exists?(card_id) ? card_id : nil)
-
end
-
-
# find +\*account card by +\*api card
-
# @param api_key [String]
-
# @return [+*account card, nil]
-
1
def find_account_by_api_key api_key
-
find_account_by :api_key, api_key.strip
-
end
-
-
# find +\*account card by +\*email card
-
# @param email [String]
-
# @return [+*account card, nil]
-
1
def find_account_by_email email
-
72
find_account_by :email, email.strip.downcase
-
end
-
-
# general pattern for finding +\*account card based on field cards
-
# @param fieldcode [Symbol] code of account field
-
# @param value [String] content of field
-
# @return [+*account card, nil]
-
1
def find_account_by fieldcode, value
-
72
Auth.as_bot do
-
72
Card.search({ right_id: Card::AccountID,
-
right_plus: [Card::Codename.id(fieldcode), { content: value }] },
-
"find +:account with +#{fieldcode} (#{value})").first
-
end
-
end
-
-
1
def session_user
-
442
session[session_user_key]
-
end
-
-
1
def set_session_user card_id
-
524
session[session_user_key] = card_id
-
end
-
-
1
def session_user_key
-
966
"user_#{database.underscore}".to_sym
-
end
-
-
1
def database
-
966
Rails.configuration.database_configuration.dig Rails.env, "database"
-
end
-
-
# set the id of the current user.
-
1
def current_id= card_id
-
566
@current = @as_id = @as_card = @current_roles = nil
-
566
card_id = card_id.to_i if card_id.present?
-
566
@current_id = card_id
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Auth
-
# singleton permission methods
-
1
module Permissions
-
# user has "root" permissions
-
# @return [true/false]
-
1
def always_ok?
-
6082
usr_id = as_id
-
6082
case usr_id
-
1090
when Card::WagnBotID then true # cannot disable
-
when nil then false
-
else
-
4992
always_ok_usr_id? usr_id
-
end
-
end
-
-
# specified user has root permission
-
# @param usr_id [Integer]
-
# @return [true/false]
-
1
def always_ok_usr_id? usr_id, force_cache_update=false
-
4992
always = always_cache
-
4992
if always[usr_id].nil? || force_cache_update
-
88
update_always_cache usr_id, admin?(usr_id)
-
else
-
4904
always[usr_id]
-
end
-
end
-
-
1
def update_always_cache usr_id, value
-
89
always = always_cache
-
89
always = always.dup if always.frozen?
-
89
always[usr_id] = value
-
89
Card.cache.write "ALWAYS", always
-
89
value
-
end
-
-
1
def always_cache
-
5081
Card.cache.read("ALWAYS") || {}
-
end
-
-
# list of names of cardtype cards that current user has perms to create
-
# @return [Array of strings]
-
1
def createable_types
-
type_names =
-
12
Auth.as_bot do
-
12
Card.search(
-
{ type: Card::CardtypeID, return: :name,
-
not: { codename: ["in"] + Card.config.non_createable_types } },
-
"find createable types"
-
)
-
end
-
-
12
type_names.select do |name|
-
531
Card.new(type: name).ok? :create
-
end.sort
-
end
-
-
# test whether user is an administrator
-
# @param user_id [Integer]
-
# @return [true/false]
-
1
def admin? user_id
-
88
has_role? user_id, Card::AdministratorID
-
end
-
-
1
def has_role? user_id, role_id
-
88
return false unless user_id && role_id
-
-
88
Card[user_id].all_enabled_roles.include? role_id
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Auth
-
# mechanism for assuming permissions of another user.
-
1
module Proxy
-
# operate with the permissions of another "proxy" user
-
1
def as given_user
-
2409
tmp_id = @as_id
-
2409
tmp_card = @as_card
-
-
2409
@as_id = get_user_id(given_user)
-
2409
@as_card = nil
-
# we could go ahead and set as_card if given a card...
-
-
2409
@current_id = @as_id if @current_id.nil?
-
-
2409
return unless block_given?
-
-
2407
yield
-
ensure
-
2409
if block_given?
-
2407
@as_id = tmp_id
-
2407
@as_card = tmp_card
-
end
-
end
-
-
# operate with the permissions of WagnBot (administrator)
-
1
def as_bot &block
-
2311
as Card::WagnBotID, &block
-
end
-
-
# id of proxy user
-
# @return [Integer]
-
1
def as_id
-
16698
@as_id || current_id
-
end
-
-
# proxy user card
-
# @return [Card]
-
1
def as_card
-
2007
if @as_card && @as_card.id == as_id
-
1746
@as_card
-
else
-
261
@as_card = Card[as_id]
-
end
-
end
-
-
# get card id from args of unknown type
-
# @todo replace with general mechanism, eg #quick_fetch
-
1
def get_user_id user
-
2409
case user
-
when NilClass then nil
-
81
when Card then user.id
-
2328
when Integer then user
-
else Card.fetch_id(user)
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Auth
-
# singleton methods for managing setup state
-
1
module Setup
-
1
NEEDS_SETUP = "NEEDS_SETUP".freeze
-
-
# app is not totally set up yet
-
# @return [true/false]
-
1
def needs_setup?
-
76
if @needs_setup == false || Card.cache.read(NEEDS_SETUP)&.to_s == "false"
-
70
@needs_setup = false
-
else
-
6
needs_setup_if_no_accounts
-
end
-
end
-
-
# for testing setup
-
1
def simulate_setup! mode=true
-
2
Card.cache.delete NEEDS_SETUP
-
2
@needs_setup = nil
-
2
@hidden_accounts = mode ? user_account_ids : nil
-
end
-
-
1
def instant_account_activation
-
simulate_needs_setup!
-
yield
-
ensure
-
simulate_needs_setup! false
-
end
-
-
1
private
-
-
1
def needs_setup_if_no_accounts
-
6
user_account_count.zero?.tap do |need|
-
6
Card.cache.write NEEDS_SETUP, false unless need
-
end
-
end
-
-
1
def user_account_ids
-
2
as_bot { Card.search user_account_cql.merge(return: :id) }
-
end
-
-
1
def user_account_cql
-
# every deck starts with two accounts: WagnBot and Anonymous
-
7
{ right_id: Card::AccountID, creator_id: ["ne", Card::WagnBotID] }
-
end
-
-
1
def user_account_count
-
6
cql = user_account_cql
-
6
cql[:not] = { id: ["in"].concat(@hidden_accounts) } if @hidden_accounts
-
12
as_bot { Card.count_by_cql cql }
-
end
-
end
-
end
-
end
-
1
require "jwt"
-
-
1
class Card
-
1
module Auth
-
# methods for setting current account
-
1
module Token
-
1
SECRET_KEY = Rails.application.secrets.secret_key_base.to_s
-
-
1
class << self
-
1
def encode user_id, extra_payload={}
-
4
payload = { user_id: user_id, exp: expiration }.merge(extra_payload)
-
-
4
JWT.encode payload, SECRET_KEY
-
end
-
-
# returns Hash if valid, String error message if not
-
-
1
def validate! token
-
2
payload = decode token
-
2
raise Card::Error::PermissionDenied, payload if payload.is_a? String
-
-
2
payload
-
end
-
-
1
def decode token
-
4
decoded = JWT.decode(token, SECRET_KEY)[0]
-
4
HashWithIndifferentAccess.new decoded
-
rescue JWT::DecodeError => error
-
error.message
-
end
-
-
1
def expiration
-
4
Card.config.token_expiry.from_now.to_i
-
end
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
1
class << self
-
1
def cache
-
273172
Card::Cache[Card]
-
end
-
end
-
-
# The {Cache} class manages and integrates {Temporary} and {Persistent}
-
# caching. The {Temporary} cache is typically process- and request- specific
-
# and is often "ahead" of the database; the {Persistent} cache is typically
-
# shared across processes and tends to stay true to the database.
-
#
-
# Any ruby Class can declare and/or retrieve its own cache as follows:
-
#
-
# ```` Card::Cache[MyClass] ````
-
#
-
# Typically speaking, mod developers do not need to use the Cache classes
-
# directly, because caching is automatically handled by Card#fetch
-
#
-
1
class Cache
-
1
extend Card::Cache::Prepopulate
-
-
1
@@cache_by_class = {}
-
1
cattr_reader :cache_by_class
-
-
1
class << self
-
1
attr_accessor :no_renewal
-
-
# create a new cache for the ruby class provided
-
# @param klass [Class]
-
# @return [{Card::Cache}]
-
1
def [] klass
-
354730
raise "nil klass" if klass.nil?
-
-
354730
cache_type = persistent_cache || nil
-
354730
cache_by_class[klass] ||= new class: klass, store: cache_type
-
end
-
-
1
def persistent_cache
-
355305
return @persistent_cache unless @persistent_cache.nil?
-
-
@persistent_cache =
-
case
-
1
when ENV["NO_RAILS_CACHE"] then false
-
1
when Cardio.config.persistent_cache then Cardio.cache
-
else false
-
end
-
end
-
-
# clear the temporary caches and ensure we're using the latest stamp
-
# on the persistent caches.
-
1
def renew
-
537
return if no_renewal
-
537
renew_persistent
-
537
cache_by_class.each_value do |cache|
-
2021
cache.soft.reset
-
2021
cache.hard&.renew
-
end
-
end
-
-
1
def renew_persistent
-
537
Card::Cache::Persistent.renew if persistent_cache
-
end
-
-
# reset standard cached for all classes
-
1
def reset
-
30
reset_hard
-
30
reset_soft
-
end
-
-
# reset all caches for all classes
-
1
def reset_all
-
reset_hard
-
reset_soft
-
reset_other
-
end
-
-
# completely wipe out all caches, often including the Persistent cache of
-
# other decks using the same mechanism.
-
# Generally prefer {.reset_all}
-
# @see .reset_all
-
1
def reset_global
-
cache_by_class.each_value do |cache|
-
cache.soft.reset
-
cache.hard&.annihilate
-
end
-
reset_other
-
end
-
-
# reset the Persistent cache for all classes
-
1
def reset_hard
-
38
Card::Cache::Persistent.reset if persistent_cache
-
38
cache_by_class.each_value do |cache|
-
140
cache.hard&.reset
-
end
-
end
-
-
# reset the Temporary cache for all classes
-
1
def reset_soft
-
178
cache_by_class.each_value { |cache| cache.soft.reset }
-
end
-
-
# reset Codename cache and delete tmp files
-
# (the non-standard caches)
-
1
def reset_other
-
Card::Codename.reset_cache
-
Cardio.delete_tmp_files!
-
end
-
-
# generate a cache key from an object
-
# @param obj [Object]
-
# @return [String]
-
1
def obj_to_key obj
-
case obj
-
when Hash
-
obj.sort.map { |key, value| "#{key}=>(#{obj_to_key(value)})" } * ","
-
when Array
-
obj.map { |value| obj_to_key(value) }
-
else
-
obj.to_s
-
end
-
end
-
end
-
-
1
attr_reader :hard, :soft
-
-
# Cache#new initializes a {Temporary}/soft cache, and -- if a :store opt
-
# is provided -- a {Persistent}/hard cache
-
# @param opts [Hash]
-
# @option opts [Rails::Cache] :store
-
# @option opts [Constant] :class
-
1
def initialize opts={}
-
4
@klass = opts[:class]
-
4
cache_by_class[@klass] = self
-
4
@hard = Persistent.new opts if opts[:store]
-
4
@soft = Temporary.new
-
end
-
-
# read cache value (and write to soft cache if missing)
-
# @param key [String]
-
1
def read key
-
134957
@soft.read(key) ||
-
28475
(@hard && (ret = @hard.read(key)) && @soft.write(key, ret))
-
end
-
-
# write to hard (where applicable) and soft cache
-
# @param key [String]
-
# @param value
-
1
def write key, value
-
9763
@hard&.write key, value
-
9763
@soft.write key, value
-
end
-
-
# read and (if not there yet) write
-
# @param key [String]
-
1
def fetch key, &block
-
108215
@soft.fetch(key) { @hard ? @hard.fetch(key, &block) : yield }
-
end
-
-
# delete specific cache entries by key
-
# @param key [String]
-
1
def delete key
-
1513
@hard&.delete key
-
1513
@soft.delete key
-
end
-
-
# reset both caches (for a given Card::Cache instance)
-
1
def reset
-
@hard&.reset
-
@soft.reset
-
end
-
-
# test for the existence of the key in either cache
-
# @return [true/false]
-
1
def exist? key
-
@soft.exist?(key) || (@hard&.exist?(key))
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
1
class Cache
-
# _Persistent_ (or _Hard_) caches closely mirror the database and are
-
# intended to be altered only upon database alterations.
-
#
-
# Unlike the database, the persistent cache stores records of records that
-
# have been requested but are missing or, in the case of some {Card cards},
-
# "virtual", meaning that they follow known patterns but do not exist in the
-
# database.
-
#
-
# Most persistent cache implementations cannot store objects with singleton
-
# classes, therefore {Card cards} generally must have set_modules
-
# re-included after retrieval from the persistent cache.
-
#
-
1
class Persistent
-
1
attr_accessor :prefix
-
-
1
class << self
-
# name of current database; used here to insure that different databases
-
# are cached separately
-
# TODO: find better home for this method
-
1
def database_name
-
580
@database_name ||= (cfg = Cardio.config) &&
-
1
(dbcfg = cfg.database_configuration) &&
-
dbcfg[Rails.env]["database"]
-
end
-
-
1
def stamp
-
1425
@stamp ||= Cardio.cache.fetch(stamp_key) { new_stamp }
-
end
-
-
# stamp generator
-
1
def new_stamp
-
183
Time.now.to_i.to_s(36) + rand(999).to_s(36)
-
end
-
-
1
def stamp_key
-
576
"#{database_name}-stamp"
-
end
-
-
1
def renew
-
537
@stamp = nil
-
end
-
-
1
def reset
-
38
@stamp = new_stamp
-
38
Cardio.cache.write stamp_key, @stamp
-
end
-
end
-
-
# @param opts [Hash]
-
# @option opts [Rails::Cache] :store
-
# @option opts [ruby Class] :class, typically ActiveRecord descendant
-
# @option opts [String] :database
-
1
def initialize opts
-
4
@store = opts[:store]
-
4
@klass = opts[:class]
-
4
@class_key = @klass.to_s.to_name.key
-
4
@database = opts[:database] || self.class.database_name
-
end
-
-
# renew insures you're using the most current cache version by
-
# reaffirming the stamp and prefix
-
1
def renew
-
2021
@stamp = nil
-
2021
@prefix = nil
-
end
-
-
# reset effectively clears the cache by setting a new stamp. However
-
# unlike annihilate, it won't bother other apps using the same cache engine.
-
1
def reset
-
140
@stamp = self.class.new_stamp
-
140
@prefix = nil
-
140
Cardio.cache.write stamp_key, @stamp
-
end
-
-
# the nuclear option. can affect other applications sharing the same
-
# cache engine. keep in mind mutually assured destruction.
-
1
def annihilate
-
@store.clear
-
end
-
-
# the current time stamp. changing this value effectively resets
-
# the cache. Note that Cardio.cache is a simple Rails::Cache, not
-
# a Card::Cache object.
-
1
def stamp
-
1356
@stamp ||= Cardio.cache.fetch(stamp_key) { self.class.new_stamp }
-
end
-
-
# key for looking up the current stamp
-
1
def stamp_key
-
1424
"#{@database}-#{@class_key}-#{self.class.stamp}-stamp"
-
end
-
-
# prefix added to cache key to create a system-wide unique key
-
1
def prefix
-
67764
@prefix ||= "#{@database}-#{@class_key}-#{stamp}:"
-
end
-
-
# returns prefix/key
-
# @param key [String]
-
# @return [String]
-
1
def full_key key
-
67764
"#{prefix}/#{key}"
-
end
-
-
1
def read key
-
28995
@store.read full_key(key)
-
end
-
-
# update an attribute of an object already in the cache
-
# @param key [String]
-
# @param attribute [String, Symbol]
-
1
def write_attribute key, attribute, value
-
return value unless @store
-
-
if (object = deep_read key)
-
object.instance_variable_set "@#{attribute}", value
-
write key, object
-
end
-
value
-
end
-
-
1
def deep_read key
-
520
local_cache = @store.send :local_cache
-
520
local_cache&.clear
-
520
read key
-
end
-
-
1
def read_attribute key, attribute
-
520
object = deep_read key
-
520
object.instance_variable_get "@#{attribute}"
-
end
-
-
1
def write key, value
-
9763
@store.write full_key(key), value
-
end
-
-
1
def fetch key, &block
-
27491
@store.fetch full_key(key), &block
-
end
-
-
1
def delete key
-
1515
@store.delete full_key(key)
-
end
-
-
1
def exist? key
-
@store.exist? full_key(key)
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Cache
-
# pre-populate cache for testing purposes
-
1
module Prepopulate
-
1
def restore
-
reset_soft
-
prepopulate
-
end
-
-
1
private
-
-
1
def prepopulate?
-
Cardio.config.prepopulate_cache
-
end
-
-
1
def prepopulate
-
return unless prepopulate?
-
-
prepopulate_rule_caches
-
# prepopulate_lexicon_caches
-
end
-
-
1
def prepopulate_cache variable
-
@prepopulated ||= {}
-
value = @prepopulated[variable] ||= yield
-
Card.cache.soft.write variable, value.clone
-
end
-
-
# def prepopulate_lexicon_caches
-
# end
-
-
1
def prepopulate_rule_caches
-
prepopulate_cache("RULES") { Card::Rule.rule_cache }
-
prepopulate_cache("READRULES") { Card::Rule.read_rule_cache }
-
prepopulate_cache("PREFERENCES") { Card::Rule.preference_cache }
-
end
-
-
# def prepopulate_card_cache
-
# prepopulate_cache "ALL_CARDS" do
-
# Card.find_each do |card|
-
# Card.write_to_cache card
-
# end
-
# true
-
# end
-
# end
-
end
-
end
-
end
-
1
class Card
-
1
class Cache
-
# The {Temporary} cache is intended for a single request, script,
-
# migration, etc. It allows you to alter a card and then retrieve
-
# the card with those alterations intact _without_ saving those
-
# changes to the database.
-
#
-
# In practice, it's a set of Cache-like methods for using a
-
# simple Hash.
-
#
-
# Unlike the Persistent cache, the Temporary cache can handle objects with
-
# singleton classes.
-
1
class Temporary
-
1
attr_reader :store
-
-
1
def initialize
-
4
@store = {}
-
end
-
-
# @param key [String]
-
1
def read key
-
217636
return unless @store.key? key
-
-
163360
@store[key]
-
end
-
-
# @param key [String]
-
1
def write key, value
-
58081
@store[key] = value
-
end
-
-
# @param key [String]
-
1
def fetch key, &_block
-
80724
read(key) || write(key, yield)
-
end
-
-
# @param key [String]
-
1
def delete key
-
5515
@store.delete key
-
end
-
-
1
def dump
-
@store.each do |k, v|
-
p "#{k} --> #{v.inspect[0..30]}"
-
end
-
end
-
-
1
def reset
-
2161
@store = {}
-
end
-
-
# @param key [String]
-
1
def exist? key
-
@store.key? key
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
# {Card}'s names can be changed, and therefore _names_ should not be directly mentioned
-
# in code, lest a name change break the application.
-
#
-
# Instead, a {Card} that needs specific code manipulations should be given a {Codename},
-
# which will not change even if the card's name does.
-
#
-
# An administrator might add to the Company card via the RESTful web API with a url like
-
#
-
# /update/CARDNAME?card[codename]=CODENAME
-
#
-
# ...or via the api like
-
#
-
# Card[CARDNAME].update! codename: CODENAME
-
#
-
# Generally speaking, _codenames_ are represented by Symbols.
-
#
-
# The {Codename} class provides a fast cache for this slow-changing data.
-
# Every process maintains a complete cache that is not frequently reset
-
#
-
1
class Codename
-
1
class << self
-
# returns codename for id and id for codename
-
# @param codename [Integer, Symbol, String, Card::Name]
-
# @return [Symbol]
-
1
def [] codename
-
102101
case codename
-
when Integer
-
80200
codehash[codename]
-
when Symbol, String
-
codehash.key?(codename.to_sym) ? codename.to_sym : nil
-
end
-
end
-
-
1
def id codename
-
9640
case codename
-
when Symbol, String
-
9640
codehash[codename.to_sym]
-
when Integer
-
codehash.key?(codename) ? codename : nil
-
end
-
end
-
-
1
def name codename=nil
-
1316
return super() if codename.nil?
-
-
1316
name! codename
-
rescue Error::CodenameNotFound => _e
-
yield if block_given?
-
end
-
-
1
def card codename
-
11
if (card_id = id(codename))
-
11
Card[card_id]
-
elsif block_given?
-
yield
-
end
-
end
-
-
1
def exist? codename
-
653
id(codename).present?
-
end
-
-
1
alias_method :exists?, :exist?
-
-
# a Hash in which Symbol keys have Integer values and vice versa
-
# @return [Hash]
-
1
def codehash
-
89847
@codehash ||= load_codehash
-
end
-
-
# clear cache both locally and in cache
-
1
def reset_cache
-
6
@codehash = nil
-
6
::Card.cache.delete "CODEHASH"
-
end
-
-
# @param codename [Symbol, String]
-
# @return [Integer]
-
1
def id! codename
-
8889
id(codename) || unknown_codename!(codename)
-
end
-
-
# @param codename [Symbol, String]
-
# @return [Card::Name]
-
1
def name! codename
-
1316
Card::Name[codename.to_sym]
-
end
-
-
1
def generate_id_constants
-
# If a card has the codename _example_, then Card::ExampleID will
-
# return the id for that card.
-
7
codehash.each do |codename, id|
-
4130
next unless codename.is_a?(Symbol) && !codename.to_s.match?(/\W/)
-
-
2037
id_constant codename, id
-
end
-
end
-
-
1
private
-
-
# iterate through every card with a codename
-
# @yieldparam codename [Symbol]
-
# @yieldparam id [Integer]
-
1
def each_codenamed_card
-
7
sql = "select id, codename from cards where codename is not NULL"
-
7
ActiveRecord::Base.connection.select_all(sql).each do |row|
-
2065
yield row["codename"].to_sym, row["id"].to_i
-
end
-
end
-
-
# @todo remove duplicate checks here; should be caught upon creation
-
1
def check_duplicates codehash, codename, card_id
-
2065
return unless codehash.key?(codename) || codehash.key?(card_id)
-
-
Rails.logger.debug "dup codename: #{codename}, "\
-
"ID:#{card_id} (#{codehash[codename]})"
-
end
-
-
# generate Hash for @codehash and put it in the cache
-
1
def load_codehash
-
7
::Card.cache.fetch("CODEHASH") do
-
7
generate_codehash
-
end
-
end
-
-
1
def generate_codehash
-
7
hash = {}
-
7
each_codenamed_card do |codename, card_id|
-
2065
check_duplicates hash, codename, card_id
-
2065
hash[codename] = card_id
-
2065
hash[card_id] = codename
-
end
-
7
hash
-
end
-
-
1
def unknown_codename! mark
-
raise Card::Error::CodenameNotFound, I18n.t(:exception_unknown_codename,
-
scope: "lib.card.codename",
-
codename: mark)
-
end
-
-
1
def id_constant codename, id=nil
-
2037
id ||= id! codename
-
2328
Card.const_get_or_set(codename.to_s.camelize + "ID") { id }
-
end
-
end
-
-
1
generate_id_constants
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
# Content objects support the parsing of content strings into arrays that
-
# contain semantically meaningful "chunks" like nests, links, urls, etc.
-
#
-
# Each chunk has an object whose class inherits from {Card::Content::Chunk::Abstract}
-
#
-
1
class Content < SimpleDelegator
-
1
extend Clean
-
1
extend Truncate
-
-
1
Chunk # trigger autoload
-
-
1
attr_reader :revision, :format, :chunks, :opts
-
-
# initialization parses String, detects chunks
-
# @param content [String]
-
# @param format_or_card [Card::Format or Card]
-
# @param opts [Hash]
-
# @option opts [Symbol] :chunk_list - name of registered list of chunk
-
# classes to be used in parsing
-
1
def initialize content, format_or_card, opts={}
-
2894
@format = resolve_format format_or_card
-
2894
opts ||= {}
-
2894
chunk_list = opts[:chunk_list] || @format.chunk_list
-
2894
@chunks = Parser.new(chunk_list, self).parse(content)
-
2894
super(@chunks.any? ? @chunks : content)
-
end
-
-
# Content must be associated with a Format object, which in turn must be
-
# associated with a Card
-
# @return [Card]
-
1
def card
-
5394
@format.card
-
end
-
-
# Find all chunks of a given type
-
# @param chunk_type [Chunk Class]
-
# @return [Array of Chunk instances]
-
1
def find_chunks chunk_type
-
524
each_chunk.select { |chunk| chunk.is_a?(chunk_type) }
-
end
-
-
1
def has_chunk? chunk_type
-
each_chunk.any { |chunk| chunk.is_a?(chunk_type) }
-
end
-
-
# sends &block to #process_chunk on each Chunk object
-
1
def process_chunks &block
-
2601
return custom_process_chunks(&block) if block_given?
-
-
2153
each_chunk(&:process_chunk)
-
end
-
-
1
def custom_process_chunks
-
448
each_chunk do |chunk|
-
1792
chunk.burn_after_reading yield(chunk)
-
end
-
end
-
-
1
def pieces
-
Array.wrap(__getobj__)
-
end
-
-
1
def each_chunk
-
3141
return enum_for(:each_chunk) unless block_given?
-
-
iterator =
-
2896
case __getobj__
-
when Hash then :each_value
-
2003
when Array then :each
-
893
when String then return # no chunks
-
else
-
Rails.logger.warn "unrecognized type for #each_chunk: " \
-
" #{self.class} #{__getobj__.class}"
-
return
-
end
-
14261
send(iterator) { |item| yield item if item.is_a?(Chunk::Abstract) }
-
end
-
-
# convert content to String.
-
# the common cases here are that either
-
#
-
# - (a) content is already a String, or
-
# - (b) it's an Array that needs to be iterated over and converted into a
-
# a string by running to_s on each item.
-
1
def to_s
-
2651
case __getobj__
-
1837
when Array then map(&:to_s) * ""
-
814
when String then __getobj__
-
when NilClass then "" # raise "Nil Card::Content"
-
else __getobj__.to_s
-
end
-
end
-
-
1
def inspect
-
"<#{__getobj__.class}:#{card}:#{self}>"
-
end
-
-
1
def without_nests
-
without_chunks Chunk::Nest do |content|
-
yield content
-
end
-
end
-
-
1
def without_references
-
50
without_chunks Chunk::Nest, Chunk::Link do |content|
-
50
yield content
-
end
-
end
-
-
1
def without_chunks *chunk_classes
-
50
chunk_classes = ::Set.new Array.wrap(chunk_classes)
-
50
stash = stash_chunks chunk_classes
-
50
processed = yield to_s
-
50
unstash_chunks processed, stash
-
end
-
-
1
private
-
-
1
def stash_chunks chunk_classes
-
50
chunks = []
-
50
each_chunk do |chunk|
-
143
next unless chunk_classes.include? chunk.class
-
-
135
chunk.burn_after_reading "{{#{chunks.size}}}"
-
135
chunks << chunk.text
-
end
-
50
chunks
-
end
-
-
1
def unstash_chunks content, stashed_chunks
-
50
Chunk::Nest.gsub content do |nest_content|
-
143
number?(nest_content) ? stashed_chunks[nest_content.to_i] : "{{#{nest_content}}}"
-
end
-
end
-
-
1
def resolve_format format_or_card
-
2894
if format_or_card.is_a?(Card)
-
241
Format.new format_or_card, format: nil
-
else
-
2653
format_or_card
-
end
-
end
-
-
1
def number? str
-
143
true if Float(str)
-
rescue StandardError
-
8
false
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
require "uri/common"
-
-
1
class Card
-
1
class Content < SimpleDelegator
-
# A chunk is a pattern of text that can be protected
-
# and interrogated by a format. Each Chunk class has a
-
# +pattern+ that states what sort of text it matches.
-
# Chunks are initalized by passing in the result of a
-
# match by its pattern.
-
#
-
1
module Chunk
-
1
mattr_accessor :raw_list, :prefix_regexp_by_list,
-
:prefix_map_by_list, :prefix_map_by_chunkname
-
1
@@raw_list = {}
-
1
@@prefix_regexp_by_list = {}
-
1
@@prefix_map_by_chunkname = {}
-
1
@@prefix_map_by_list = Hash.new { |h, k| h[k] = {} }
-
-
1
class << self
-
1
def register_class klass, hash
-
9
klass.config = hash.merge class: klass
-
9
prefix_index = hash[:idx_char] || :default
-
# ^ this is gross and needs to be moved out.
-
-
9
klassname = klass.name.split("::").last.to_sym
-
9
prefix_map_by_chunkname[klassname] = { prefix_index => klass.config }
-
9
raw_list.each do |key, list|
-
54
next unless list.include? klassname
-
-
15
prefix_map_by_list[key].merge! prefix_map_by_chunkname[klassname]
-
end
-
end
-
-
1
def register_list key, list
-
6
raw_list[key] = list
-
6
prefix_map_by_list[key] =
-
list.each_with_object({}) do |chunkname, h|
-
15
next unless (p_map = prefix_map_by_chunkname[chunkname])
-
-
h.merge! p_map
-
end
-
end
-
-
1
def find_class_by_prefix prefix, chunk_list_key=:default
-
6047
validate_chunk_list_key chunk_list_key
-
-
6047
prefix_map = prefix_map_by_list[chunk_list_key]
-
6047
config = prefix_map[prefix[0, 1]] ||
-
prefix_map[prefix[-1, 1]] ||
-
prefix_map[:default]
-
# prefix identified by first character, last character, or default.
-
# a little ugly...
-
-
6047
config[:class]
-
end
-
-
1
def prefix_regexp chunk_list_key
-
2894
prefix_regexp_by_list[chunk_list_key] ||=
-
build_prefix_regexp chunk_list_key
-
end
-
-
1
def build_prefix_regexp chunk_list_key
-
5
validate_chunk_list_key chunk_list_key
-
-
prefix_res =
-
5
raw_list[chunk_list_key].map do |chunkname|
-
14
chunk_class = const_get chunkname
-
14
chunk_class.config[:prefix_re]
-
end
-
5
/(?:#{ prefix_res * '|' })/m
-
end
-
-
1
def validate_chunk_list_key chunk_list_key
-
6052
unless raw_list.key? chunk_list_key
-
raise ArgumentError, "invalid chunk list key: #{chunk_list_key}"
-
end
-
end
-
end
-
-
# not sure whether this is best place.
-
# Could really happen almost anywhere
-
# (even before chunk classes are loaded).
-
1
register_list :default, %i[
-
URI HostURI EmailURI EscapedLiteral Nest Link
-
]
-
1
register_list :references, %i[EscapedLiteral Nest Link]
-
1
register_list :nest_only, [:Nest]
-
1
register_list :query, [:QueryReference]
-
1
register_list :stub, [:ViewStub]
-
1
register_list :references_keep_escaping, %i[KeepEscapedLiteral Nest Link]
-
end
-
end
-
1
Card::Mod::Loader.load_chunks
-
end
-
1
class Card
-
1
class Content < SimpleDelegator
-
# A chunk is a pattern of text that can be protected
-
# and interrogated by a format. Each Chunk class has a
-
# +pattern+ that states what sort of text it matches.
-
# Chunks are initalized by passing in the result of a
-
# match by its pattern.
-
#
-
1
module Chunk
-
1
class Abstract
-
1
class_attribute :config
-
1
attr_reader :text, :process_chunk
-
-
1
class << self
-
# if the prefix regex matched check that chunk against the full regex
-
1
def full_match content, prefix=nil
-
6047
content.match full_re(prefix)
-
end
-
-
1
def full_re _prefix
-
6031
config[:full_re]
-
end
-
-
1
def context_ok? _content, _chunk_start
-
6041
true
-
end
-
end
-
-
1
def reference_code
-
126
"I"
-
end
-
-
1
def initialize match, content
-
6047
match = self.class.full_match(match) if match.is_a? String
-
6047
@text = match[0]
-
6047
@processed = nil
-
6047
@content = content
-
6047
interpret match, content
-
end
-
-
1
def interpret _match_string, _content
-
Rails.logger.info "no #interpret method found for chunk class: " \
-
"#{self.class}"
-
end
-
-
1
def format
-
14646
@content.format
-
end
-
-
1
def card
-
5394
@content.card
-
end
-
-
1
def to_s
-
5758
result
-
end
-
-
1
def result
-
5758
burn_read || @process_chunk || @processed || @text
-
end
-
-
1
def burn_read
-
5758
return unless @burn_read
-
-
1927
tmp = @burn_read
-
1927
@burn_read = nil
-
1927
tmp
-
end
-
-
# Temporarily overrides the processed nest content for single-use
-
# After using the nest's result
-
# (for example via `to_s`) the original result is restored
-
1
def burn_after_reading text
-
1927
@burn_read = text
-
end
-
-
1
def inspect
-
"<##{self.class}##{self}>"
-
end
-
-
1
def as_json _options={}
-
burn_read || @process_chunk || @processed ||
-
"not rendered #{self.class}, #{card&.name}"
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Content
-
# tools for cleaning content, especially for restricing unwanted HTML
-
1
module Clean
-
1
allowed_tags = {}
-
%w[
-
1
br i b pre cite caption strong em ins sup sub del ol hr ul li p
-
div h1 h2 h3 h4 h5 h6 span table tr td th tbody thead tfoot
-
32
].each { |tag| allowed_tags[tag] = [] }
-
-
# allowed attributes
-
1
allowed_tags.merge!(
-
"a" => %w[href title target],
-
"img" => %w[src alt title],
-
"code" => ["lang"],
-
"blockquote" => ["cite"]
-
)
-
-
1
if Cardio.config.allow_inline_styles
-
1
allowed_tags["table"] += %w[cellpadding align border cellspacing data-mce-style]
-
1
allowed_tags["td"] += %w[scope data-mce-style]
-
1
allowed_tags["th"] += %w[scope data-mce-style]
-
end
-
-
1
allowed_tags.each_key do |k|
-
36
allowed_tags[k] << "class"
-
36
allowed_tags[k] << "style" if Cardio.config.allow_inline_styles
-
36
allowed_tags[k]
-
end
-
-
1
ALLOWED_TAGS = allowed_tags.freeze
-
-
1
ATTR_VALUE_RE = [/(?<=^')[^']+(?=')/, /(?<=^")[^"]+(?=")/, /\S+/].freeze
-
-
1
def clean! string, tags=ALLOWED_TAGS
-
207
cleaned = clean_tags string, tags
-
207
cleaned = clean_spaces cleaned if Cardio.config.space_last_in_multispace
-
207
cleaned
-
end
-
-
1
private
-
-
## Method that cleans the String of HTML tags
-
## and attributes outside of the allowed list.
-
1
def clean_tags string, ok_tags
-
# $LAST_MATCH_INFO is nil if string is a SafeBuffer
-
207
string.to_str.gsub(%r{<(/*)(\w+)([^>]*)>}) do |_raw|
-
100
clean_tag $LAST_MATCH_INFO, ok_tags
-
end.gsub(/<\!--.*?-->/, "")
-
end
-
-
1
def clean_spaces string
-
207
string.gsub(/(?:^|\b) ((?: )+)/, '\1 ')
-
end
-
-
1
def clean_tag match, ok_tags
-
100
tag = match[2].downcase
-
100
return " " unless (ok_attrs = ok_tags[tag])
-
100
"<#{match[1]}#{html_attribs tag, match[3], ok_attrs}>"
-
end
-
-
1
def html_attribs tag, raw_attr, ok_attrs
-
100
ok_attrs.each_with_object([tag]) do |ok_attr, pcs|
-
200
q, rest_value = process_attribute ok_attr, raw_attr
-
200
pcs << "#{ok_attr}=#{q}#{rest_value}#{q}" unless rest_value.blank?
-
end * " "
-
end
-
-
1
def process_attribute attrib, all_attributes
-
200
return ['"', nil] unless all_attributes =~ /\b#{attrib}\s*=\s*(?=(.))/i
-
-
q = '"'
-
rest_value = $'
-
if (idx = %w[' "].index Regexp.last_match(1))
-
q = Regexp.last_match(1)
-
end
-
reg_exp = ATTR_VALUE_RE[idx || 2]
-
rest_value = process_attribute_match rest_value, reg_exp, attrib
-
[q, rest_value]
-
end
-
-
# NOTE allows classes beginning with "w-" (deprecated)
-
1
def process_attribute_match rest_value, reg_exp, attrib
-
return rest_value unless (match = rest_value.match reg_exp)
-
-
rest_value = match[0]
-
if attrib == "class"
-
rest_value.split(/\s+/).select { |s| s =~ /^w-/i }.join(" ")
-
else
-
rest_value
-
end
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
1
class Content
-
1
class Diff
-
1
class << self
-
1
def complete a, b, opts={}
-
Card::Content::Diff.new(a, b, opts).complete
-
end
-
-
1
def summary a, b, opts={}
-
Card::Content::Diff.new(a, b, opts).summary
-
end
-
-
1
def render_added_chunk text
-
8
"<ins class='diffins diff-added'>#{text}</ins>"
-
end
-
-
1
def render_deleted_chunk text, _count=true
-
12
"<del class='diffdel diff-deleted'>#{text}</del>"
-
end
-
end
-
-
1
attr_reader :result
-
1
delegate :summary, :complete, :summary_omits_content?, to: :result
-
-
# diff options
-
# :format => :html|:text|:pointer|:raw
-
# :html = maintain html structure, but compare only content
-
# :text = remove all html tags; compare plain text
-
# :pointer = remove all double square brackets
-
# :raw = escape html tags and compare everything
-
#
-
# summary: {length: <number> , joint: <string> }
-
1
def initialize old_version, new_version, opts={}
-
2
@result = Result.new opts[:summary]
-
2
return unless new_version
-
-
2
lcs_opts = lcs_opts_for_format opts[:diff_format]
-
2
LCS.new(lcs_opts).run(old_version, new_version, @result)
-
end
-
-
1
def red?
-
@result.dels_cnt > 0
-
end
-
-
1
def green?
-
@result.adds_cnt > 0
-
end
-
-
1
private
-
-
1
def lcs_opts_for_format diff_format
-
2
opts = {}
-
2
case diff_format
-
when :html
-
opts[:exclude] = /^</
-
when :text
-
2
opts[:reject] = /^</
-
4
opts[:postprocess] = proc { |word| word.gsub("\n", "<br>") }
-
when :pointer
-
opts[:preprocess] = proc { |word| word.gsub("[[", "").gsub("]]", "<br>") }
-
else # :raw
-
opts[:preprocess] = proc { |word| CGI.escapeHTML(word) }
-
end
-
2
opts
-
end
-
end
-
end
-
end
-
# require "card/content/diff/processor"
-
-
1
class Card
-
1
class Content
-
1
class Diff
-
# Use LCS algorithm to create a {Diff::Result}
-
1
class LCS
-
1
def initialize opts
-
# regex; remove matches completely from diff
-
2
@reject_pattern = opts[:reject]
-
# regex; put matches back to the result after diff
-
2
@exclude_pattern = opts[:exclude]
-
-
2
@preprocess = opts[:preprocess] # block; called with every word
-
2
@postprocess = opts[:postprocess] # block; called with complete diff
-
-
2
@splitters = %w(<[^>]+> \[\[[^\]]+\]\] \{\{[^}]+\}\} \s+)
-
2
@disjunction_pattern = /^\s/
-
end
-
-
1
def run old_text, new_text, result
-
2
@result = result
-
2
compare old_text, new_text
-
2
@result.complete = postprocess @result.complete
-
end
-
-
1
private
-
-
1
def compare old_text, new_text
-
2
if old_text
-
2
old_words, old_ex = separate_comparables_from_excludees old_text
-
2
new_words, new_ex = separate_comparables_from_excludees new_text
-
2
processor = Processor.new old_words, new_words, old_ex, new_ex
-
2
processor.run @result
-
else
-
list = split_and_preprocess(new_text)
-
list = list.reject { |word| word.match @exclude_pattern } if @exclude_pattern
-
# CAUTION: postproces and added_chunk changed order
-
# and no longer postprocess for summary
-
@result.write_added_chunk list.join
-
end
-
end
-
-
1
def separate_comparables_from_excludees text
-
# return two arrays, one with all words, one with pairs
-
# (index in word list, html_tag)
-
4
list = split_and_preprocess text
-
4
if @exclude_pattern
-
check_exclude_and_disjunction_pattern list
-
else
-
4
[list, []]
-
end
-
end
-
-
1
def check_exclude_and_disjunction_pattern list
-
list.each_with_index.each_with_object([[], []]) do |pair, res|
-
element, index = pair
-
if element.match? @disjunction_pattern
-
res[1] << { chunk_index: index, element: element, type: :disjunction }
-
elsif element.match? @exclude_pattern
-
res[1] << { chunk_index: index, element: element, type: :excludee }
-
else
-
res[0] << element
-
end
-
end
-
end
-
-
1
def split_and_preprocess text
-
4
splitted = split_to_list_of_words(text).select do |s|
-
16
!s.empty? && (!@reject_pattern || !s.match(@reject_pattern))
-
end
-
4
@preprocess ? splitted.map { |s| @preprocess.call(s) } : splitted
-
end
-
-
1
def split_to_list_of_words text
-
4
split_regex = /(#{@splitters.join '|'})/
-
4
text.split(split_regex)
-
end
-
-
1
def preprocess text
-
@preprocess ? @preprocess.call(text) : text
-
end
-
-
1
def postprocess text
-
2
@postprocess ? @postprocess.call(text) : text
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Content
-
1
class Diff
-
1
class LCS
-
# Compares two lists of chunks and generates a diff
-
1
class Processor
-
1
def initialize old_words, new_words, old_excludees, new_excludees
-
2
@adds = []
-
2
@dels = []
-
2
@words = { old: old_words, new: new_words }
-
@excludees =
-
2
ExcludeeIterator.old_and_new old_excludees, new_excludees
-
end
-
-
1
def run result
-
2
@result = result
-
2
prev_action = nil
-
2
::Diff::LCS.traverse_balanced(@words[:old], @words[:new]) do |word|
-
10
process_word word, prev_action
-
10
prev_action = word.action
-
end
-
2
write_all
-
2
@result
-
end
-
-
1
def process_word word, prev_action
-
10
prev_action ? interpret_action(prev_action, word.action) : write_excludees
-
10
process_element word.old_element, word.new_element, word.action
-
end
-
-
1
def interpret_action prev_actn, word_actn
-
8
handle_action?(word_actn, prev_actn) ? handle_action(word_actn) : write_all
-
end
-
-
1
def handle_action? word_action, prev_action
-
8
(prev_action == word_action) ||
-
6
(prev_action == "-" && word_action == "!") ||
-
6
(prev_action == "!" && word_action == "+")
-
end
-
-
1
def handle_action action
-
2
case action
-
2
when "-" then del_old_excludees
-
when "+" then add_new_excludees
-
when "!" then
-
del_old_excludees
-
add_new_excludees
-
else
-
write_excludees
-
end
-
end
-
-
1
def write_all
-
8
write_dels
-
8
write_adds
-
8
write_excludees
-
end
-
-
1
def write_unchanged text
-
2
@result.write_unchanged_chunk text
-
end
-
-
1
def write_dels
-
8
return if @dels.empty?
-
-
6
@result.write_deleted_chunk @dels.join
-
6
@dels = []
-
end
-
-
1
def write_adds
-
8
return if @adds.empty?
-
-
4
@result.write_added_chunk @adds.join
-
4
@adds = []
-
end
-
-
1
def write_excludees
-
20
while (ex = @excludees[:new].next)
-
@result.write_excluded_chunk ex[:element]
-
end
-
end
-
-
1
def del_old_excludees
-
2
@excludees[:old].scan_and_record(@dels) do |element|
-
write_dels
-
@result.write_excluded_chunk element
-
end
-
end
-
-
1
def add_new_excludees
-
@excludees[:new].scan_and_record(@adds) do |element|
-
write_adds
-
@result.complete << element
-
end
-
end
-
-
1
def process_element old_element, new_element, action
-
10
case action
-
4
when "-" then minus old_element
-
when "+" then plus new_element
-
when "!"
-
4
minus old_element
-
4
plus new_element
-
else
-
2
write_unchanged new_element
-
2
@excludees[:new].word_step
-
end
-
end
-
-
1
def plus new_element
-
4
@adds << new_element
-
4
@excludees[:new].word_step
-
end
-
-
1
def minus old_element
-
8
@dels << old_element
-
8
@excludees[:old].word_step
-
end
-
end
-
-
# support class for LCS::Processor
-
1
class ExcludeeIterator
-
1
def self.old_and_new old_excludees, new_excludees
-
{
-
2
old: new(old_excludees),
-
new: new(new_excludees)
-
}
-
end
-
-
1
def initialize list
-
4
@list = list
-
4
@index = 0
-
4
@chunk_index = 0
-
end
-
-
1
def word_step
-
14
@chunk_index += 1
-
end
-
-
1
def next
-
12
if @index < @list.size &&
-
@list[@index][:chunk_index] == @chunk_index
-
res = @list[@index]
-
@index += 1
-
@chunk_index += 1
-
res
-
end
-
end
-
-
1
def scan_and_record record_array
-
4
while (ex = self.next)
-
if ex[:type] == :disjunction
-
record_array << ex[:element]
-
else
-
yield ex[:element]
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Content
-
1
class Diff
-
# Result object for Diff processing
-
1
class Result
-
1
attr_accessor :complete, :summary, :dels_cnt, :adds_cnt
-
1
def initialize summary_opts=nil
-
2
@dels_cnt = 0
-
2
@adds_cnt = 0
-
2
@complete = ""
-
2
@summary = Summary.new summary_opts
-
end
-
-
1
def summary
-
2
@summary.rendered
-
end
-
-
1
def summary_omits_content?
-
2
@summary.omits_content?
-
end
-
-
1
def write_added_chunk text
-
4
@adds_cnt += 1
-
4
@complete << Card::Content::Diff.render_added_chunk(text)
-
4
@summary.add text
-
end
-
-
1
def write_deleted_chunk text
-
6
@dels_cnt += 1
-
6
@complete << Card::Content::Diff.render_deleted_chunk(text)
-
6
@summary.delete text
-
end
-
-
1
def write_unchanged_chunk text
-
2
@complete << text
-
2
@summary.omit
-
end
-
-
1
def write_excluded_chunk text
-
@complete << text
-
end
-
-
# Summary object for Diff processing
-
1
class Summary
-
1
def initialize opts
-
2
opts ||= {}
-
2
@remaining_chars = opts[:length] || 50
-
2
@joint = opts[:joint] || "..."
-
-
2
@summary = nil
-
2
@chunks = []
-
2
@content_omitted = false
-
end
-
-
1
def rendered
-
2
@summary ||=
-
begin
-
2
truncate_overlap
-
2
@chunks.map do |chunk|
-
12
@content_omitted ||= chunk[:action] == :ellipsis
-
12
render_chunk chunk[:action], chunk[:text]
-
end.join
-
end
-
end
-
-
1
def add text
-
4
add_chunk text, :added
-
end
-
-
1
def delete text
-
6
add_chunk text, :deleted
-
end
-
-
1
def omit
-
2
if @chunks.empty? || @chunks.last[:action] != :ellipsis
-
2
add_chunk @joint, :ellipsis
-
end
-
end
-
-
1
def omits_content?
-
2
@content_omitted || @remaining_chars < 0
-
end
-
-
1
private
-
-
1
def add_chunk text, action
-
12
if @remaining_chars > 0
-
12
@chunks << { action: action, text: text }
-
12
@remaining_chars -= text.size
-
end
-
end
-
-
1
def render_chunk action, text
-
12
case action
-
when "+", :added
-
4
Card::Content::Diff.render_added_chunk text
-
when "-", :deleted
-
6
Card::Content::Diff.render_deleted_chunk text
-
2
else text
-
end
-
end
-
-
1
def truncate_overlap
-
2
return unless @remaining_chars < 0
-
-
process_ellipsis
-
-
index = @chunks.size - 1
-
while @remaining_chars < @joint.size && index >= 0
-
overlap_size = @remaining_chars + @chunks[index][:text].size
-
break if process_overlap overlap_size, index
-
-
index -= 1
-
end
-
end
-
-
1
def process_ellipsis
-
return unless @chunks.last[:action] == :ellipsis
-
-
@chunks.pop
-
@content_omitted = true
-
@remaining_chars += @joint.size
-
end
-
-
1
def process_overlap overlap_size, index
-
if overlap_size == @joint.size
-
replace_with_joint index
-
true
-
elsif overlap_size > @joint.size
-
cut_with_joint index
-
true
-
else
-
@remaining_chars += @chunks[index][:text].size
-
@chunks.delete_at(index)
-
false
-
end
-
end
-
-
1
def cut_with_joint index
-
@chunks[index][:text] =
-
@chunks[index][:text][0..(@remaining_chars - @joint.size - 1)]
-
@chunks[index][:text] += @joint
-
end
-
-
1
def replace_with_joint index
-
@chunks.pop
-
if index - 1 >= 0
-
if @chunks[index - 1][:action] == :added
-
@chunks << { action: :ellipsis, text: @joint }
-
elsif @chunks[index - 1][:action] == :deleted
-
@chunks << { action: :added, text: @joint }
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
# require "card/content/chunk"
-
-
1
class Card
-
1
class Content
-
# The Content::Parser breaks content strings into an Array of "chunks",
-
# each of which may be an instance of a {Chunk} class or a simple String.
-
1
class Parser
-
# @param chunk_list [Symbol] name of registered list of chunk classes
-
# to be used in parsing
-
# @see Card::Chunk.register_list
-
# @param content_object [Card::Content]
-
1
def initialize chunk_list, content_object
-
2894
@content_object = content_object
-
2894
@chunk_list = chunk_list
-
end
-
-
# break content string into an array of chunk objects and strings
-
# @param content [String]
-
# @return [Array]
-
1
def parse content
-
2894
@content = content
-
2894
@chunks = []
-
2894
return @chunks unless content.is_a? String
-
-
2894
@position = @last_position = 0
-
2894
@interval_string = ""
-
2894
parse_chunks
-
2894
@chunks
-
end
-
-
1
private
-
-
1
def parse_chunks
-
2894
prefix_regexp = Chunk.prefix_regexp @chunk_list
-
2894
match_prefices prefix_regexp
-
2894
handle_remainder
-
end
-
-
1
def match_prefices prefix_regexp
-
2894
while match_prefix prefix_regexp
-
6047
@chunk_class = Chunk.find_class_by_prefix @prefix, @chunk_list
-
# get the chunk class from the prefix
-
6047
content_slice = @content[@position..-1]
-
6047
@match, @offset = @chunk_class.full_match content_slice, @prefix
-
# see whether the full chunk actually matches
-
# (as opposed to bogus prefix)
-
6047
if @match # we have a chunk match
-
6047
next if record_chunk
-
else # no match. look at the next character
-
@position += 1
-
end
-
@interval_string += @content[@chunk_start..@position - 1]
-
# moving beyond the alleged chunk.
-
# append failed string to "nonchunk" string
-
end
-
end
-
-
1
def match_prefix prefix_regexp
-
8941
prefix_match = @content[@position..-1].match(prefix_regexp)
-
8941
if prefix_match
-
6047
@prefix = prefix_match[0]
-
# prefix of matched chunk
-
6047
@chunk_start = prefix_match.begin(0) + @position
-
# content index of beginning of chunk
-
6047
if prefix_match.begin(0) > 0
-
# if matched chunk is not beginning of test string
-
5003
@interval_string += @content[@position..@chunk_start - 1]
-
# hold onto the non-chunk part of the string
-
end
-
6047
@position = @chunk_start
-
# move scanning position up to beginning of chunk
-
6047
true
-
end
-
end
-
-
1
def record_chunk
-
6047
@position += (@match.end(0) - @offset.to_i)
-
# move scanning position up to end of chunk
-
6047
if !@chunk_class.context_ok? @content, @chunk_start
-
# make sure there aren't contextual reasons for ignoring this chunk
-
false
-
else
-
6047
@chunks << @interval_string unless @interval_string.empty?
-
6047
@interval_string = ""
-
# add the nonchunk string to the chunk list and
-
# reset interval string for next go-round
-
6047
@chunks << @chunk_class.new(@match, @content_object)
-
# add the chunk to the chunk list
-
6047
@last_position = @position
-
# note that the end of the chunk was the last place where a
-
# chunk was found (so far)
-
6047
true
-
end
-
end
-
-
1
def handle_remainder
-
2894
if @chunks.any? && @last_position < @content.size
-
# handle any leftover nonchunk string at the end of content
-
1200
@chunks << @content[@last_position..-1]
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Content
-
# tools for truncating content
-
1
module Truncate
-
1
ELLIPSES_HTML = '<span class="closed-content-ellipses">...</span>'.freeze
-
-
1
def smart_truncate input, words=25
-
return if input.nil?
-
-
truncated, wordstring = truncate input, words
-
# nuke partial tags at end of snippet
-
wordstring.gsub!(/(<[^\>]+)$/, "")
-
wordstring = close_tags wordstring
-
wordstring += ELLIPSES_HTML if truncated
-
# wordstring += '...' if wordlist.length > l
-
polish wordstring
-
end
-
-
1
private
-
-
1
def truncate input, words
-
wordlist = input.to_s.split
-
l = words.to_i - 1
-
l = 0 if l.negative?
-
truncating = wordlist.length > l
-
wordstring = truncating ? wordlist[0..l].join(" ") : input.to_s
-
[truncating, wordstring]
-
end
-
-
1
def close_tags wordstring
-
tags = find_tags wordstring
-
tags.each { |t| wordstring += "</#{t}>" }
-
wordstring
-
end
-
-
1
def polish wordstring
-
wordstring.gsub! %r{<[/]?br[\s/]*>}, " "
-
# Also a hack -- get rid of <br>'s -- they make line view ugly.
-
wordstring.gsub! %r{<[/]?p[^>]*>}, " "
-
## Also a hack -- get rid of <br>'s -- they make line view ugly.
-
wordstring
-
end
-
-
1
def find_tags wordstring
-
tags = []
-
-
# match tags with or without self closing (ie. <foo />)
-
wordstring.scan(%r{\<([^\>\s/]+)[^\>]*?\>}).each do |t|
-
tags.unshift(t[0])
-
end
-
# match tags with self closing and mark them as closed
-
wordstring.scan(%r{\<([^\>\s/]+)[^\>]*?/\>}).each do |t|
-
next unless (x = tags.index(t[0]))
-
-
tags.slice!(x)
-
end
-
# match close tags
-
wordstring.scan(%r{\</([^\>\s/]+)[^\>]*?\>}).each do |t|
-
next unless (x = tags.rindex(t[0]))
-
-
tags.slice!(x)
-
end
-
tags
-
end
-
end
-
end
-
end
-
1
class Card
-
# Directs the symphony of a card {Card::Act **act**}.
-
#
-
# Each act is divided into {Card::Action **actions**}: one action for each card.
-
# There are three action types: _create_, _update_, and _delete_.
-
#
-
# Each action is divided into three **phases**: _validation_, _storage_, and
-
# _integration_.
-
#
-
# Each phase is divided into three **stages**, as follows:
-
#
-
# #### Validation Stages
-
#
-
# * __VI__: initialize
-
# * __VP__: prepare_to_validate
-
# * __VV__: validate
-
#
-
# #### Storage Stages
-
#
-
# * __SP__: prepare_to_store
-
# * __SS__: store
-
# * __SF__: finalize
-
#
-
# #### Integration Stages
-
#
-
# * __II__: integrate
-
# * __IA__: after_integrate
-
# * __ID__: integrate_with_delay
-
#
-
# And each stage can have many {Card::Set::Event::Api **events**}, each of which is
-
# defined using the {Card::Set::Event::Api Event API}.
-
#
-
# The table below gives you an overview events can/should do in each stage:
-
#
-
# | Phase: | validation | storage | integration
-
# | Stage: | VI - VP - VV | SP - SS - SF | II - IA - ID
-
# |---------------------------| :---: | :---: |:---:
-
# | **tasks**
-
# | attach subcard | yes! yes! yes | yes yes yes | yes yes no
-
# | detach subcard | yes! yes! yes | yes no no! | no!
-
# | validate | yes yes yes! | no | no
-
# | insecure change [^1] | yes yes! no | no! | no!
-
# | secure change [^2] | yes | yes! no! no! | no!
-
# | abort | yes! | yes | yes
-
# | add errors | yes! | no! | no!
-
# | subsave | yes | yes | yes!
-
# | has id (new card) | no | no no? yes | yes
-
# | within web request | yes | yes | yes yes no
-
# | within transaction [^3] | yes | yes | no
-
# | **values**
-
# | dirty attributes | yes | yes | yes
-
# | params | yes | yes | yes
-
# | success | yes | yes | yes
-
# | session | yes | yes | yes yes no
-
#
-
# #### Understanding the Table
-
#
-
# - **yes!** the recommended stage to do that
-
# - **yes** ok to do it here
-
# - **no** not recommended; risky but not guaranteed to fail
-
# - **no!** never do it here. it won't work or will break things
-
#
-
# If there is only a single entry in a phase column it counts for all stages
-
# of that phase
-
#
-
# [^1]: 'insecure' means a change that might make the card invalid to save
-
# [^2]: 'secure' means you're sure that the change won't invalidate the card
-
# [^3]: If an exception is raised in the validation or storage phase
-
# everything will rollback. If an integration event fails, db changes
-
# of the other two phases will remain persistent, and other integration
-
# events will continue to run.
-
#
-
# ## Director, Directors, and Subdirectors
-
#
-
# Only one act can be performed at a time in any given Card process. Information about
-
# that act is managed by _Director class methods_. Every act is associated with a
-
# single "main" card.
-
#
-
# The act, however, may involve many cards/actions. Each action has its own _Director
-
# instance_ that leads the card through all its stages. When a card action (A1)
-
# initiates a new action on a different card (A2), a new Director object is initialized.
-
# The new A2 subdirector's @parent is the director of the A1 card. Conversely, the
-
# A1 card stores a SubdirectorArray in @subdirectors to give it access to A2's
-
# Director and any little Director babies to which it gave birth.
-
#
-
# Subdirectors follow one of two distinct patterns:
-
#
-
# 1. {Card::Subcards **Subcards**}. When a card is altered using the subcards API, the
-
# director follows a "breadth-first" pattern. For each stage a card runs its
-
# stage events and then triggers its subcards to run that stage before proceeding
-
# to the next stage. If a subcard is added in a stage then by the end of that
-
# stage the director will catch it up to the current stage.
-
# 2. **Subsaves**. When a card is altered by a direct save (`Card.create(!)`,
-
# `card.update(!)`, `card.delete(!)`, `card.save(!)`...), then the validation and
-
# storage phases are executed immediately (depth-first), returning the saved card.
-
# The integration phase, however, is executed following the same pattern as with
-
# subcards.
-
#
-
# Let's consider a subcard example. Suppose you define the following event on
-
# self/bar.rb
-
#
-
# event :met_a_foo_at_the_bar, :prepare_to_store, on: :update do
-
# add_subcard "foo"
-
# end
-
#
-
# And then you run `Card[:bar].update!({})`.
-
#
-
# When bar reaches the event in its `prepare_to_store` stage, the "foo" subcard will be
-
# added. After that stage ends, the stages `initialize`, `prepare_to_validate`,
-
# `validate`, and `prepare_to_store` are executed for foo so that it is now caught
-
# up with Bar at the `prepare_to_store` stage.
-
#
-
# If you have subcards within subcards, stages are executed preorder depth-first.
-
#
-
# Eg, assuming:
-
#
-
# - A has subcards AA and AB
-
# - AA has subcard AAA
-
# - AB has subcard ABA
-
#
-
# ...then the order of execution is:
-
#
-
# 1. A
-
# 2. AA
-
# 3. AAA
-
# 4. AB
-
# 5. ABA
-
#
-
# A special case can happen in the store stage when a supercard needs a subcard's id
-
# (for example as left_id or as type_id) and the subcard doesn't have an id yet
-
# (because it gets created in the same act). In this case the subcard's store stage
-
# is executed BEFORE the supercard's store stage.
-
#
-
# ---
-
1
class Director
-
1
extend EventDelay
-
1
extend ActDirection
-
-
1
include Stages
-
1
include Phases
-
1
include Run
-
1
include Store
-
-
1
attr_accessor :act, :card, :stage, :parent, :subdirectors, :head
-
1
attr_reader :running
-
1
alias_method :running?, :running
-
-
1
def initialize card, parent
-
515
@card = card
-
515
@card.director = self
-
# for read actions there is no validation phase
-
# so we have to set the action here
-
515
@stage = nil
-
515
@running = false
-
515
@prepared = false
-
515
@parent = parent
-
515
@subdirectors = SubdirectorArray.initialize_with_subcards(self)
-
515
register
-
end
-
-
1
def main?
-
6920
parent.nil?
-
end
-
-
1
def head?
-
4058
@head || main?
-
end
-
-
1
def register
-
515
Director.add self
-
end
-
-
1
def unregister
-
Director.delete self
-
end
-
-
1
def delete
-
24
@parent&.subdirectors&.delete self
-
24
@card.director = nil
-
24
@subdirectors.clear
-
24
@stage = nil
-
24
@action = nil
-
end
-
-
1
def appoint card
-
98
reset_stage
-
98
update_card card
-
98
@head = true
-
end
-
-
1
def abort
-
88
@abort = true
-
end
-
-
1
def need_act
-
362
act_director = main_director
-
362
raise Card::Error, "act requested without a main director" unless act_director
-
-
362
@act = act_director.act ||= Director.need_act
-
end
-
-
1
def main_director
-
362
return self if main?
-
-
183
Director.act_director || (@parent&.main_director)
-
end
-
-
1
def to_s level=1
-
str = @card.name.to_s.clone
-
if @subdirectors.present?
-
subs = subdirectors.map { |d| " " * level + d.to_s(level + 1) }.join "\n"
-
str << "\n#{subs}"
-
end
-
str
-
end
-
-
1
def replace_card card
-
1
card.action = @card.action
-
1
card.director = self
-
1
@card = card
-
1
reset_stage
-
1
catch_up_to_stage @stage if @stage
-
end
-
-
1
def update_card card
-
98
old_card = @card
-
98
@card = card
-
98
Director.card_changed old_card
-
end
-
end
-
end
-
1
class Card
-
1
class Director
-
1
module ActDirection
-
1
attr_accessor :act, :act_card
-
-
1
def act_director
-
183
return unless act_card
-
-
183
act_card.director
-
end
-
-
1
def directors
-
2806
@directors ||= {}
-
end
-
-
1
def run_act card
-
535
self.act_card = card
-
# add new_director(card)
-
535
yield
-
ensure
-
535
clear
-
end
-
-
1
def need_act
-
179
self.act ||= Card::Act.create ip_address: Env.ip
-
end
-
-
1
def clear
-
535
self.act_card = nil
-
535
self.act = nil
-
535
directors.each_pair do |card, _dir|
-
491
card.expire
-
491
card.director = nil
-
491
card.action = nil
-
491
card.clear_action_specific_attributes
-
end
-
535
expire
-
535
@directors = nil
-
end
-
-
1
def expire
-
642
expirees.each { |expiree| Card.expire expiree }
-
628
@expirees = []
-
end
-
-
1
def expirees
-
642
@expirees ||= []
-
end
-
-
1
def fetch card, parent=nil
-
539
return directors[card] if directors[card]
-
-
516
directors.each_key do |dir_card|
-
396
return dir_card.director if dir_card.name == card.name && dir_card.director
-
end
-
515
add new_director(card, parent)
-
end
-
-
1
def include? name
-
directors.keys.any? { |card| card.key == name.to_name.key }
-
end
-
-
1
def new_director card, parent
-
515
if !parent && act_card && act_card != card && running_act?
-
act_card.director.subdirectors.add card
-
else
-
515
Director.new card, parent
-
end
-
end
-
-
1
def card name
-
65
directors.values.find do |dir|
-
115
dir.card.name == name
-
end&.card
-
end
-
-
1
def add director
-
# Rails.logger.debug "added: #{director.card.name}".green
-
1128
directors[director.card] = director
-
end
-
-
1
def card_changed old_card
-
98
return unless (director = @directors.delete old_card)
-
-
98
add director
-
end
-
-
1
def delete director
-
24
return unless @directors
-
-
24
@directors.delete director.card
-
24
director.delete
-
end
-
-
1
def deep_delete director
-
24
director.subdirectors.each do |subdir|
-
deep_delete subdir
-
end
-
24
delete director
-
end
-
-
1
def running_act?
-
act_director&.running?
-
end
-
-
1
def to_s
-
act_director.to_s
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Director
-
1
module CardMethods
-
1
attr_writer :director
-
1
delegate :validation_phase, :storage_phase, :integration_phase,
-
:validation_phase_callback?, :integration_phase_callback?, to: :director
-
-
1
def director
-
2188
@director ||= Director.fetch self
-
end
-
-
1
def prepare_for_phases
-
474
reset_patterns
-
474
identify_action
-
474
include_set_modules
-
end
-
-
1
def identify_action
-
@action =
-
474
if trash && trash_changed?
-
12
:delete
-
462
elsif new_card?
-
308
:create
-
else
-
154
:update
-
end
-
end
-
-
1
def restore_changes_information
-
# restores changes for integration phase
-
# (rails cleared them in an after_create/after_update hook which is
-
# executed before the integration phase)
-
88
return unless saved_changes.present?
-
-
79
@mutations_from_database = mutations_before_last_save
-
end
-
-
1
def clear_action_specific_attributes
-
491
self.class.action_specific_attributes.each do |attr|
-
8347
instance_variable_set "@#{attr}", nil
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Director
-
# methods for handling delayed events
-
1
module EventDelay
-
# If active jobs (and hence the integrate_with_delay events) don't run
-
# in a background process then Card::Env.deserialize! decouples the
-
# controller's params hash and the Card::Env's params hash with the
-
# effect that params changes in the CardController get lost
-
# (a crucial example are success params that are processed in
-
# CardController#soft_redirect)
-
1
def contextualize_delayed_event act_id, card, env, auth
-
93
return yield unless delaying?
-
-
41
with_env_and_auth env, auth do
-
82
with_delay_act(act_id, card) { yield }
-
end
-
end
-
-
1
def delaying?
-
93
Cardio.config.delaying == true
-
end
-
-
1
def with_delay_act act_id, card, &block
-
41
return yield unless act_id && (self.act = Act.find act_id)
-
-
41
run_job_with_act act, card, &block
-
end
-
-
1
def run_job_with_act act, card, &block
-
41
run_act card do
-
41
act_card.director.run_delayed_event act, &block
-
end
-
end
-
-
1
def with_env_and_auth env, auth
-
41
Card::Auth.with auth do
-
41
Card::Env.with env do
-
41
yield
-
end
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
180
before_validation :validation_phase, if: -> { validation_phase_callback? }
-
1
around_save :storage_phase
-
217
after_commit :integration_phase, if: -> { integration_phase_callback? }
-
-
1
class Director
-
# Validation, Storage, and Integration phase handling
-
1
module Phases
-
1
def validation_phase_callback?
-
179
!@only_storage_phase && head?
-
end
-
-
1
def integration_phase_callback?
-
216
!@only_storage_phase && main?
-
end
-
-
1
def prepare_for_phases
-
680
@card.prepare_for_phases unless running?
-
680
@running = true
-
680
@subdirectors.each(&:prepare_for_phases)
-
end
-
-
1
def validation_phase
-
179
run_stage :initialize
-
179
run_stage :prepare_to_validate
-
169
run_stage :validate
-
ensure
-
# @card.expire_pieces if @card.errors.any?
-
179
@card.errors.empty?
-
end
-
-
# Unlike other phases, the storage phase takes a block,
-
# because it is called by an "around" callback
-
1
def storage_phase &block
-
216
catch_up_to_stage :prepare_to_store
-
216
run_stage :store, &block
-
216
run_stage :finalize
-
216
raise ActiveRecord::RecordInvalid, @card if @card.errors.any?
-
ensure
-
216
@from_trash = nil
-
end
-
-
1
def integration_phase
-
88
return if @abort
-
-
88
@card.restore_changes_information
-
88
run_stage :integrate
-
88
run_stage :after_integrate
-
88
run_stage :integrate_with_delay
-
ensure
-
88
@card.clear_changes_information unless @abort
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Director
-
# methods for running stages
-
1
module Run
-
1
def catch_up_to_stage next_stage
-
1266
return if @delay && before?(:integrate_with_delay, next_stage)
-
-
1266
upto_stage(next_stage) do |stage|
-
1552
run_stage stage
-
end
-
end
-
-
1
def run_delayed_event act
-
41
@running = true
-
41
@act = act
-
41
@stage = stage_index :integrate_with_delay
-
41
yield
-
41
run_subcard_stages :integrate_with_delay
-
end
-
-
1
def delay!
-
@delay = true
-
end
-
-
1
def restart
-
@running = false
-
@stage = nil
-
end
-
-
1
private
-
-
1
def upto_stage stage
-
1266
@stage ||= -1
-
1266
(@stage + 1).upto(stage_index(stage)) do |i|
-
1552
yield stage_symbol(i)
-
end
-
end
-
-
1
def valid_next_stage? next_stage
-
2775
@stage ||= -1
-
2775
return false if in_or_after?(next_stage) || ahead_of_parent?(next_stage)
-
2647
return false unless valid_card? next_stage
-
2647
check_skipped_stage next_stage
-
2647
true
-
end
-
-
1
def valid_card? next_stage
-
2647
@card.errors.empty? || in_or_before?(:validate, next_stage)
-
end
-
-
1
def check_skipped_stage stage
-
2647
return unless before? previous_stage_index(stage)
-
raise Card::Error, "stage #{previous_stage_symbol stage} was " \
-
"skipped for card #{@card}"
-
end
-
-
1
def ahead_of_parent? next_stage
-
2738
return false if head?
-
-
1683
after? parent.stage, next_stage
-
end
-
-
1
def run_stage stage, &block
-
2775
return true unless valid_next_stage? stage
-
-
# puts "#{@card.name}: #{stage} stage".yellow
-
2647
prepare_stage_run stage
-
2647
execute_stage_run stage, &block
-
end
-
-
1
def prepare_stage_run stage
-
2647
@stage = stage_index stage
-
2647
prepare_for_phases if stage == :initialize
-
end
-
-
1
def execute_stage_run stage, &block
-
# in the store stage it can be necessary that
-
# other subcards must be saved before we save this card
-
2647
return store(&block) if stage == :store
-
-
2303
run_stage_callbacks stage
-
2215
run_subcard_stages stage
-
2215
run_final_stage_callbacks stage
-
end
-
-
1
def run_stage_callbacks stage, callback_postfix=""
-
4734
Rails.logger.debug "#{stage}: #{@card.name}"
-
# we use abort :success in the :store stage for :save_draft
-
-
4734
callbacks = :"#{stage}#{callback_postfix}_stage"
-
4734
if in_or_before?(:store, stage) && !main?
-
3552
@card.abortable { @card.run_callbacks callbacks }
-
else
-
2958
@card.run_callbacks callbacks
-
end
-
end
-
-
1
def run_subcard_stages stage
-
2688
each_subcard_director stage do |subdir|
-
1141
condition = block_given? ? yield(subdir) : true
-
1141
subdir.catch_up_to_stage stage if condition
-
end
-
end
-
-
1
def each_subcard_director stage
-
2688
subdirectors.each do |subdir|
-
1141
yield subdir unless subdir.head? && before?(:integrate, stage)
-
end
-
ensure
-
2688
@card.handle_subcard_errors
-
end
-
-
1
def run_final_stage_callbacks stage
-
2215
run_stage_callbacks stage, "_final"
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Director
-
# Methods for intepreting stages of an action
-
1
module Stages
-
1
STAGES = %i[initialize prepare_to_validate validate
-
prepare_to_store store finalize integrate
-
after_integrate integrate_with_delay].freeze
-
1
STAGE_INDEX = STAGES.each_with_index.with_object({}) do |(stage, index), hash|
-
9
Card.define_callbacks "#{stage}_stage", "#{stage}_final_stage"
-
9
hash[stage] = index
-
end.freeze
-
-
1
def stage_symbol index
-
1552
case index
-
when Symbol
-
return index if STAGE_INDEX[index]
-
when Integer
-
1552
return STAGES[index] if index < STAGES.size
-
end
-
raise Card::Error, "not a valid stage index: #{index}"
-
end
-
-
1
def stage_index stage
-
30407
case stage
-
when Symbol then
-
20654
STAGE_INDEX[stage]
-
when Integer then
-
9753
stage
-
else
-
raise Card::Error, "not a valid stage: #{stage}"
-
end
-
end
-
-
1
def stage_ok? opts
-
return false unless stage
-
test = %i[during before after].find { |t| opts[t] }
-
test ? send("#{test}?", opts[t]) : true
-
end
-
-
1
def finished_stage? stage
-
@stage > stage_index(stage)
-
end
-
-
1
def reset_stage
-
99
@stage = -1
-
end
-
-
1
private
-
-
1
def previous_stage_index from_stage=nil
-
2647
from_stage ||= @stage
-
2647
stage_index(from_stage) - 1
-
end
-
-
1
def previous_stage_symbol from_stage=nil
-
stage_symbol previous_stage_index(from_stage)
-
end
-
-
1
def before? *args
-
5294
stage_test(*args) { |r, t| r > t }
-
end
-
-
1
def in_or_before? *args
-
9468
stage_test(*args) { |r, t| r >= t }
-
end
-
-
1
def after? *args
-
3366
stage_test(*args) { |r, t| r < t }
-
end
-
-
1
def in_or_after? *args
-
5550
stage_test(*args) { |r, t| r <= t }
-
end
-
-
1
def during? *args
-
stage_test(*args) { |r, t| r == t }
-
end
-
-
1
def stage_test reference_stage, test_stage=nil
-
11839
test_stage ||= @stage
-
11839
yield stage_index(reference_stage), stage_index(test_stage)
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Director
-
# Special handling specific to the :store stage
-
1
module Store
-
1
def after_store &block
-
44
@after_store ||= []
-
44
@after_store << block
-
end
-
-
1
protected
-
-
1
def after_store?
-
400
@after_store.present?
-
end
-
-
1
private
-
-
# The tricky part here is to preserve the dirty marks on the subcards'
-
# attributes for the finalize stage.
-
# To achieve this we can't just call the :store and :finalize callbacks on
-
# the subcards as we do in the other phases.
-
# Instead we have to call `save` on the subcards and use the ActiveRecord
-
# :around_save callback.
-
1
def store &save_block
-
344
raise Card::Error, "need block to store main card" if main? && !block_given?
-
-
# the block is the ActiveRecord block from the around save callback that
-
# saves the card
-
344
if block_given?
-
216
run_stage_callbacks :store
-
216
store_with_subcards(&save_block)
-
else
-
128
trigger_storage_phase_callback
-
end
-
end
-
-
1
def store_with_subcards
-
216
store_pre_subcards # the exception! usually does nothing
-
216
yield
-
216
run_after_store_callbacks if after_store?
-
216
store_post_subcards # the typical case
-
216
true
-
ensure
-
216
@card.handle_subcard_errors
-
end
-
-
1
def run_after_store_callbacks
-
88
@after_store.each { |block| block.call @card }
-
end
-
-
# If the subcard has an after-store callback, it means the subcard
-
# must run before the supercard and then call back
-
1
def store_pre_subcards
-
216
run_subcard_stages :store, &:after_store?
-
end
-
-
1
def store_post_subcards
-
308
run_subcard_stages(:store) { |subdir| !subdir.after_store? }
-
end
-
-
# trigger the storage_phase, skip the other phases
-
# At this point the :prepare_to_store stage was already executed
-
# by the parent director. So the storage phase will only run
-
# the :store stage and the :finalize stage
-
1
def trigger_storage_phase_callback
-
128
@stage = stage_index :prepare_to_store
-
128
@only_storage_phase = true
-
128
@card.save! validate: false, as_subcard: true
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Director
-
1
class SubdirectorArray < Array
-
1
def self.initialize_with_subcards parent
-
515
dir_array = new(parent)
-
515
parent.card.subcards.each_card do |subcard|
-
104
dir_array.add subcard
-
end
-
515
dir_array
-
end
-
-
1
def initialize parent
-
515
@parent = parent
-
515
super()
-
end
-
-
1
def add card
-
444
card = card.card if card.is_a? Director
-
444
existing(card) || fetch_new(card)
-
end
-
-
1
alias_method :delete_director, :delete
-
-
1
def delete card
-
48
if card.is_a? Director
-
24
delete_director card
-
else
-
35
delete_if { |dir| dir.card == card }
-
end
-
end
-
-
1
private
-
-
1
def existing card
-
738
find { |dir| dir.card == card }
-
end
-
-
1
def fetch_new card
-
296
Director.fetch(card, @parent).tap do |dir|
-
296
update dir, card unless dir.main?
-
end
-
end
-
-
1
def update dir, card
-
296
dir.replace_card card if dir.card != card
-
296
dir.parent = @parent
-
296
self << dir
-
end
-
end
-
end
-
end
-
1
class Card
-
# Special "dirty" handling for significant card fields.
-
1
module Dirty
-
1
extend ::Card::Dirty::MethodFactory
-
-
1
class << self
-
1
def dirty_fields
-
3
%i[name db_content trash type_id left_id right_id codename]
-
end
-
-
1
def dirty_aliases
-
3
{ type: :type_id, content: :db_content }
-
end
-
-
1
def dirty_options
-
2
dirty_fields + dirty_aliases.keys
-
end
-
end
-
-
8
dirty_fields.each { |field| define_dirty_methods field }
-
3
dirty_aliases.each { |k, v| alias_method "#{k}_is_changing?", "#{v}_is_changing?" }
-
-
1
def attribute_before_act attr
-
3945
if saved_change_to_attribute? attr
-
350
attribute_before_last_save attr
-
3595
elsif will_save_change_to_attribute? attr
-
1839
mutations_from_database.changed_values[attr]
-
1756
elsif not_in_callback?
-
54
attribute_was attr
-
else
-
1702
_read_attribute attr
-
end
-
end
-
-
1
def not_in_callback?
-
# or in integrate_with_delay stage
-
10993
mutations_before_last_save.equal?(mutations_from_database)
-
end
-
-
1
def attribute_is_changing? attr
-
9237
if not_in_callback?
-
109
attribute_changed? attr
-
else
-
9128
saved_change_to_attribute?(attr) ||
-
will_save_change_to_attribute?(attr)
-
end
-
end
-
end
-
-
# Even special-er handling for dirty cardnames
-
1
module DirtyNames
-
1
def name_is_changing?
-
2959
super || left_id_is_changing? || right_id_is_changing?
-
end
-
-
# def name_before_last_save
-
# super || dirty_name(left_id_before_last_save, right_id_before_last_save)
-
# end
-
-
1
def name_before_act
-
1400
super || dirty_name(left_id_before_act, right_id_before_act)
-
end
-
-
1
def dirty_name left, right
-
1187
return unless left.present? && right.present?
-
-
100
Card::Name[left, right]
-
end
-
end
-
end
-
1
class Card
-
1
module Dirty
-
1
module MethodFactory
-
1
def define_dirty_methods field
-
9
define_method "#{field}_before_act" do
-
3783
attribute_before_act field
-
end
-
-
9
define_method "#{field}_is_changing?" do
-
9237
attribute_is_changing? field
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
# Card::Env is a module for containing the variable details of the environment
-
# in which Card operates.
-
#
-
# Env can differ for each request; Card.config should not.
-
1
module Env
-
1
extend LocationHistory
-
1
extend RequestAssignments
-
1
extend SlotOptions
-
1
extend Serialization
-
-
1
class << self
-
1
def reset args={}
-
445
@env = { main_name: nil }
-
445
return self unless (c = args[:controller])
-
-
444
self[:controller] = c
-
444
self[:session] = c.request.session
-
444
self[:params] = c.params
-
444
self[:ip] = c.request.remote_ip
-
444
self[:ajax] = assign_ajax(c)
-
444
self[:html] = assign_html(c)
-
444
self[:host] = assign_host(c)
-
444
self[:protocol] = assign_protocol(c)
-
444
self
-
end
-
-
1
def [] key
-
46660
@env[key.to_sym]
-
end
-
-
1
def []= key, value
-
4727
@env[key.to_sym] = value
-
end
-
-
1
def params
-
37756
self[:params] ||= {}
-
end
-
-
1
def with_params hash
-
old_params = params.clone
-
params.merge! hash
-
yield
-
ensure
-
self.params = old_params
-
end
-
-
1
def hash hashish
-
789
case hashish
-
when Hash then hashish.clone
-
267
when ActionController::Parameters then hashish.to_unsafe_h
-
522
else {}
-
end
-
end
-
-
1
def session
-
2883
self[:session] ||= {}
-
end
-
-
1
def reset_session
-
6
if session.is_a? Hash
-
self[:session] = {}
-
else
-
6
self[:controller]&.reset_session
-
end
-
end
-
-
1
def success cardname=nil
-
663
self[:success] ||= Env::Success.new(cardname, params[:success])
-
end
-
-
1
def localhost?
-
self[:host]&.match?(/^localhost/)
-
end
-
-
1
def ajax?
-
2210
self[:ajax]
-
end
-
-
1
def html?
-
255
!self[:controller] || self[:html]
-
end
-
-
1
private
-
-
1
def method_missing method_id, *args
-
179
case args.length
-
179
when 0 then self[method_id]
-
when 1 then self[method_id] = args[0]
-
else super
-
end
-
end
-
end
-
end
-
end
-
-
1
Card::Env.reset
-
1
class Card
-
1
module Env
-
1
module Location
-
# card_path makes a relative path site-absolute (if not already)
-
# card_url makes it a full url (if not already)
-
-
1
def card_path rel_path
-
7930
unless rel_path.is_a? String
-
Rails.logger.warn "Pass only strings to card_path. "\
-
"(#{rel_path} = #{rel_path.class})"
-
end
-
7930
if rel_path =~ %r{^(https?\:)?/}
-
390
rel_path
-
else
-
7540
"#{Card.config.relative_url_root}/#{rel_path}"
-
end
-
end
-
-
1
def card_url rel
-
499
rel =~ /^https?\:/ ? rel : "#{protocol_and_host}#{card_path rel}"
-
end
-
-
1
def protocol_and_host
-
461
Card.config.protocol_and_host || "#{Env[:protocol]}#{Env[:host]}"
-
end
-
-
1
def cardname_from_url url
-
return unless (cardname = cardname_from_url_regexp)
-
m = url.match cardname
-
m ? Card::Name[m[:mark]] : nil
-
end
-
-
1
private
-
-
1
def cardname_from_url_regexp
-
return unless Env[:host]
-
-
%r{#{Regexp.escape Env[:host]}/(?<mark>[^\?]+)}
-
end
-
-
1
extend Location # allows calls on Location constant, eg Location.card_url
-
end
-
end
-
end
-
1
class Card
-
1
module Env
-
# session history helpers: we keep a history stack so that in the case of
-
# card removal we can crawl back up to the last un-removed location
-
1
module LocationHistory
-
1
def location_history
-
308
session[:history] ||= [Env::Location.card_path("")]
-
308
session[:history].shift if session[:history].size > 5
-
308
session[:history]
-
end
-
-
1
def save_location card
-
315
return unless save_location?(card)
-
-
95
discard_locations_for card
-
95
session[:previous_location] =
-
Env::Location.card_path card.name.url_key
-
95
location_history.push previous_location
-
end
-
-
1
def save_location? card
-
315
!Env.ajax? && Env.html? && card.known? && (card.codename != :signin)
-
end
-
-
1
def previous_location
-
104
return unless location_history
-
-
104
session[:previous_location] ||= location_history.last
-
end
-
-
1
def discard_locations_for card
-
# quoting necessary because cards have things like "+*" in the names..
-
101
session[:history] = location_history.reject do |loc|
-
201
if (url_key = url_key_for_location(loc))
-
201
url_key.to_name.key == card.key
-
end
-
end.compact
-
101
session[:previous_location] = nil
-
end
-
-
1
def save_interrupted_action uri
-
session[:interrupted_action] = uri
-
end
-
-
1
def interrupted_action
-
78
session.delete :interrupted_action
-
end
-
-
1
def url_key_for_location location
-
201
%r{/([^/]*$)} =~ location ? Regexp.last_match[1] : nil
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Env
-
# environmental variables assigned based on request
-
1
module RequestAssignments
-
1
private
-
-
1
def assign_ajax c
-
444
c.request.xhr? || c.request.params[:simulate_xhr]
-
end
-
-
1
def assign_html c
-
444
[nil, "html"].member?(c.params[:format])
-
end
-
-
1
def assign_host c
-
444
Card.config.override_host || c.request.env["HTTP_HOST"]
-
end
-
-
1
def assign_protocol c
-
444
Card.config.override_protocol || c.request.protocol
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Env
-
# serializing environment (eg for delayed jobs)
-
1
module Serialization
-
1
SERIALIZABLE_ATTRIBUTES = ::Set.new %i[
-
main_name params ip ajax html host protocol salt
-
]
-
-
# @param serialized_env [Hash]
-
1
def with serialized_env
-
41
tmp_env = serialize if @env
-
41
@env ||= {}
-
41
@env.update serialized_env
-
41
yield
-
ensure
-
41
@env.update tmp_env if tmp_env
-
end
-
-
1
def serialize
-
1556
@env.select { |k, _v| SERIALIZABLE_ATTRIBUTES.include?(k) }
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Env
-
# slot-related environmental variable handling
-
1
module SlotOptions
-
1
def slot_opts
-
# FIXME: upgrade to safe parameters
-
647
self[:slot_opts] ||= interpret_slot_options
-
end
-
-
1
private
-
-
1
def interpret_slot_options
-
316
opts = hash params[:slot]
-
316
opts.merge! shortcut_slot_opts
-
316
opts.deep_symbolize_keys.slice(*Card::View::Options.slot_keys)
-
end
-
-
1
def shortcut_slot_opts
-
316
opts = {}
-
316
opts[:size] = params[:size].to_sym if params[:size]
-
316
opts[:items] = { view: params[:item].to_sym } if slot_items_shortcut?
-
316
opts
-
end
-
-
1
def slot_items_shortcut?
-
316
params[:item].present? && !params.dig(:slot, :items, :view).present?
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Env
-
# Success objects
-
1
class Success
-
1
include Card::Env::Location
-
-
1
attr_accessor :redirect, :name, :name_context, :reload
-
1
attr_writer :params, :card
-
1
attr_reader :id
-
-
1
def initialize name_context=nil, success_args=nil
-
387
@name_context = name_context
-
387
@new_args = {}
-
387
@params = OpenStruct.new
-
387
self << normalize_success_args(success_args)
-
end
-
-
1
def in_context name_context
-
157
self.name_context = name_context
-
157
self
-
end
-
-
1
def normalize_success_args success_args
-
387
case success_args
-
when nil
-
335
self.mark = "_self"
-
335
{}
-
when ActionController::Parameters
-
1
success_args.to_unsafe_h
-
else
-
51
success_args
-
end
-
end
-
-
1
def << value
-
398
if value.is_a? Hash
-
346
apply value
-
else
-
52
self.target = value
-
end
-
end
-
-
1
def reload?
-
157
@reload.to_s == "true"
-
end
-
-
# TODO: refactor to use cardish
-
1
def mark= value
-
392
case value
-
when Integer then @id = value
-
383
when String then @name = value
-
9
when Card then @card = value
-
else
-
self.target = value
-
end
-
end
-
-
# @deprecated
-
1
def id= id
-
# for backwards compatibility use mark here.
-
# id was often used for the card name
-
1
self.mark = id
-
end
-
-
1
def type= type
-
9
@new_args[:type] = type
-
end
-
-
1
def type_id= type_id
-
@new_args[:type_id] = type_id.to_i
-
end
-
-
1
def content= content
-
@new_args[:content] = content
-
end
-
-
1
def target= value
-
62
@id = @name = @card = nil
-
62
@target = process_target value
-
end
-
-
1
def process_target value
-
113
case value
-
1
when "" then ""
-
4
when "*previous", :previous then :previous
-
1
when %r{^(http|/)} then value
-
when /^REDIRECT:\s*(.+)/
-
51
@redirect = true
-
51
process_target Regexp.last_match(1)
-
56
else self.mark = value
-
end
-
end
-
-
1
def apply hash
-
346
hash.each_pair do |key, value|
-
39
self[key] = value
-
end
-
end
-
-
1
def card name_context=@name_context
-
191
if @card
-
18
@card
-
173
elsif @id
-
Card.fetch @id
-
173
elsif @name
-
160
Card.fetch @name.to_name.absolute(name_context), new: @new_args
-
end
-
end
-
-
1
def target name_context=@name_context
-
191
card(name_context) ||
-
13
(@target == :previous ? Card::Env.previous_location : @target) ||
-
Card.fetch(name_context)
-
end
-
-
1
def []= key, value
-
39
if respond_to? "#{key}="
-
19
send "#{key}=", value
-
else
-
20
@params.send "#{key}=", value
-
end
-
end
-
-
1
def [] key
-
if respond_to? key.to_sym
-
send key.to_sym
-
else
-
@params.send key.to_sym
-
end
-
end
-
-
1
def flash message=nil
-
300
@params[:flash] ||= []
-
300
@params[:flash] << message if message
-
300
@params[:flash]
-
end
-
-
1
def params
-
151
@params.marshal_dump
-
end
-
-
1
def raw_params
-
@params
-
end
-
-
1
def to_url name_context=@name_context
-
129
case (target = target(name_context))
-
when Card
-
123
target.format.path params
-
else
-
6
target
-
end
-
end
-
-
1
def method_missing method, *args
-
case method
-
when /^(\w+)=$/
-
self[Regexp.last_match(1).to_sym] = args[0]
-
when /^(\w+)$/
-
self[Regexp.last_match(1).to_sym]
-
else
-
super
-
end
-
end
-
-
1
def session
-
Card::Env.session
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
# exceptions and errors.
-
# (arguably most of these should be Card::Exception)
-
1
class Error < StandardError
-
1
cattr_accessor :current
-
1
class_attribute :status_code, :view
-
1
attr_writer :backtrace
-
-
1
self.view = :errors
-
1
self.status_code = 422
-
-
1
attr_accessor :card
-
-
1
def initialize message=nil
-
3
if message.is_a? Card
-
self.card = message
-
message = message_from_card
-
end
-
3
super message
-
end
-
-
1
def message_from_card
-
I18n.t :exception_for_card,
-
scope: %i[lib card error], cardname: card.name, message: card_message_text
-
end
-
-
1
def backtrace
-
6
@backtrace || super
-
end
-
-
1
def report
-
3
Rails.logger.info "exception = #{self.class}: #{message}"
-
3
Rails.logger.debug backtrace.join("\n")
-
end
-
-
1
def card_message_text
-
card.errors.first&.message
-
end
-
-
# error attributable to code (as opposed to card configuration)
-
1
class ServerError < Error
-
1
def self.view
-
debugger_on? ? :debug_server_error : :server_error
-
end
-
-
1
def self.status_code
-
# Errors with status code 900 are displayed as modal instead of inside
-
# the "card-notice" div``
-
debugger_on? ? 900 : 500
-
end
-
-
1
def self.debugger_on?
-
Card::Codename[:debugger] && Card[:debugger]&.content =~ /on/
-
end
-
-
1
def report
-
super
-
card&.notable_exception_raised
-
end
-
end
-
-
# error whose message can be shown to any user
-
1
class UserError < Error
-
1
cattr_accessor :user_error_classes
-
1
self.user_error_classes = [self,
-
ActionController::BadRequest,
-
ActionController::MissingFile,
-
ActiveRecord::RecordNotFound,
-
ActiveRecord::RecordInvalid]
-
end
-
-
# error in CQL query
-
1
class BadQuery < UserError
-
end
-
-
1
class BadAddress < UserError
-
1
self.status_code = 404
-
1
self.view = :bad_address
-
end
-
-
# card not found
-
1
class NotFound < UserError
-
1
self.status_code = 404
-
1
self.view = :not_found
-
end
-
-
1
class CodenameNotFound < NotFound
-
end
-
-
# two editors altering the same card at once
-
1
class EditConflict < UserError
-
1
self.status_code = 409
-
1
self.view = :conflict
-
end
-
-
# permission errors
-
1
class PermissionDenied < UserError
-
1
self.status_code = 403
-
1
self.view = :denial
-
-
1
def card_message_text
-
card.errors[:permission_denied]
-
end
-
end
-
-
# exception class for aborting card actions
-
1
class Abort < StandardError
-
1
attr_reader :status
-
-
1
def report
-
Rails.logger.debug "aborting: #{message}"
-
end
-
-
1
def initialize status, msg=""
-
88
@status = status
-
88
super msg
-
end
-
end
-
-
# associating views with exceptions
-
1
class << self
-
1
KEY_MAP = { permission_denied: PermissionDenied,
-
conflict: EditConflict }.freeze
-
-
1
def report exception, card
-
3
e = cardify_exception exception, card
-
3
self.current = e
-
3
e.report
-
3
e
-
end
-
-
1
def cardify_exception exception, card
-
card_exception =
-
3
if exception.is_a? Card::Error
-
exception
-
else
-
3
card_error_class(exception, card).new exception.message
-
end
-
3
card_exception.card ||= card
-
3
card_exception.backtrace ||= exception.backtrace
-
3
add_card_errors card, card_exception if card.errors.empty?
-
3
card_exception
-
end
-
-
1
def add_card_errors card, exception
-
label = exception.class.to_s.split("::").last
-
card.errors.add label, exception.message
-
end
-
-
1
def card_error_class exception, card
-
# "simple" error messages are visible to end users and are generally not
-
# treated as software bugs (though they may be "shark" bugs)
-
3
case exception
-
when ActiveRecord::RecordInvalid
-
3
invalid_card_error_class card
-
when ActiveRecord::RecordNotFound, ActionController::MissingFile
-
Card::Error::NotFound
-
else
-
Card::Error::ServerError
-
end
-
end
-
-
1
def invalid_card_error_class card
-
3
KEY_MAP.each do |key, klass|
-
6
return klass if card.errors.key? key
-
end
-
1
Card::Error
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
# Card::Format and its subclasses ({Card::Format::HtmlFormat},
-
# {Card::Format::JsonFormat}, {Card::Format::XmlFormat}, etc)
-
# are responsible for defining and rendering _views_.
-
#
-
# However, monkeys (those who code in the Card/Decko framework) rarely write code
-
# directly in these classes. Instead they organize their code using {Card::Mod mods}.
-
#
-
# {Card::Mod} explains how to set up a mod.
-
# {Card::Set::Format} explains how to use this and other format classes within a mod.
-
# {Card::Set::Format::AbstractFormat} introduces the view API, which is organized with
-
# these format classes.
-
#
-
1
class Format
-
1
extend ActiveSupport::Autoload
-
1
extend Registration
-
-
1
include Card::Env::Location
-
1
include Nesting
-
1
include Render
-
1
include Wrapper
-
1
include ContextNames
-
1
include Content
-
1
include Error
-
1
include MethodDelegation
-
-
1
cattr_accessor :registered, :aliases
-
1
self.registered = []
-
1
self.aliases = {}
-
-
1
attr_reader :card, :parent, :main_opts, :modal_opts
-
1
attr_accessor :form, :error_status, :rendered
-
-
1
def self.view_caching?
-
true
-
end
-
-
1
def initialize card, opts={}
-
7007
@card = card
-
7007
require_card_to_initialize!
-
20163
opts.each { |key, value| instance_variable_set "@#{key}", value }
-
7007
include_set_format_modules
-
end
-
-
1
def require_card_to_initialize!
-
7007
return if @card
-
-
msg = I18n.t :exception_init_without_card, scope: "lib.card.format"
-
raise Card::Error, msg
-
end
-
-
1
def include_set_format_modules
-
7007
self.class.format_ancestry.reverse_each do |klass|
-
14456
card.set_format_modules(klass).each do |m|
-
25869
singleton_class.send :include, m
-
end
-
end
-
end
-
-
1
def page controller, view, slot_opts
-
315
@controller = controller
-
315
context_names # loads names and removes #name_context from slot_opts
-
315
@card.run_callbacks :show_page do
-
315
show view, slot_opts
-
end
-
end
-
-
1
def params
-
3656
Env.params
-
end
-
-
1
def controller
-
425
@controller || Env[:controller] ||= CardController.new
-
end
-
-
1
def session
-
Env.session
-
end
-
-
1
def mime_type
-
"text/plain"
-
end
-
-
1
def escape_literal literal
-
literal
-
end
-
-
1
def to_sym
-
Card::Format.format_sym self
-
end
-
end
-
end
-
1
class Card
-
1
class Format
-
1
module Content
-
1
def process_content override_content=nil, content_opts=nil, &block
-
2601
content_obj = content_object override_content , content_opts
-
2601
content_obj.process_chunks(&block)
-
2601
content_obj.to_s
-
end
-
-
# Preserves the syntax in all nests. The content is yielded with placeholders
-
# for all nests. After executing the given block the original nests are put back in.
-
# Placeholders are numbers in double curly brackets like {{2}}.
-
1
def safe_process_content override_content=nil, content_opts=nil, &block
-
content_obj =
-
50
content_object override_content, chunk_list: :references_keep_escaping
-
50
result = content_obj.without_references(&block)
-
50
process_content result, content_opts
-
end
-
-
# nested by another card's content
-
# (as opposed to a direct API nest)
-
1
def content_nest opts={}
-
3646
return opts[:comment] if opts.key? :comment # commented nest
-
-
3646
nest_name = opts[:nest_name]
-
3646
return main_nest(opts) if main_nest?(nest_name) && @nest_mode != :template
-
-
3422
nest nest_name, opts
-
end
-
-
1
def format_date date, include_time=true
-
# using DateTime because Time doesn't support %e on some platforms
-
if include_time
-
# .strftime('%B %e, %Y %H:%M:%S')
-
I18n.localize(DateTime.new(date.year, date.mon, date.day,
-
date.hour, date.min, date.sec),
-
format: :card_date_seconds)
-
else
-
# .strftime('%B %e, %Y')
-
I18n.localize(DateTime.new(date.year, date.mon, date.day),
-
format: :card_date_only)
-
end
-
end
-
-
1
def add_class options, klass
-
8526
return if klass.blank?
-
-
7224
options[:class] = css_classes options[:class], klass
-
end
-
-
1
alias_method :append_class, :add_class
-
-
1
def prepend_class options, klass
-
41
options[:class] = css_classes klass, options[:class]
-
end
-
-
1
def css_classes *array
-
7272
array.flatten.uniq.compact * " "
-
end
-
-
1
def id_counter
-
2640
return @parent.id_counter if @parent
-
-
719
@id_counter ||= 0
-
719
@id_counter += 1
-
end
-
-
1
def unique_id
-
719
"#{card.name.safe_key}-#{id_counter}"
-
end
-
-
1
def output *content
-
18528
content = yield if block_given?
-
18528
Array.wrap(content).flatten.compact.join "\n"
-
end
-
-
1
private
-
-
1
def content_object content=nil, content_opts=voo&.content_opts
-
2651
return content if content.is_a? Card::Content
-
-
2651
content ||= render_raw || ""
-
2651
Card::Content.new content, self, content_opts
-
end
-
-
-
end
-
end
-
end
-
1
class Card
-
1
class Format
-
# Contextual names make titles less noisy by not rendering redundant name parts
-
#
-
# For example, in the context of "Ball", "Ball+size" is rendered as just "+size"
-
1
module ContextNames
-
1
def naming name=nil
-
248
result = yield
-
248
add_name_context name
-
248
result
-
end
-
-
# TODO: stop this lazy loading
-
# the combo of lazy loading + format ancestry navigation + caching is dangerous
-
# Long term, it would probably be smarter to handle this in the voo.
-
1
def context_names
-
4876
@context_names ||= initial_context_names
-
end
-
-
1
def initial_context_names
-
3952
@initial_context_names ||= relevant_context_names do
-
2885
parent ? parent.context_names : context_names_from_params
-
end
-
end
-
-
1
def relevant_context_names
-
2885
part_keys = @card.name.part_names.map(&:key)
-
4046
yield.select { |n| part_keys.include? n.key }
-
end
-
-
# "slot[name_context]" param is a string; @context_names is an array
-
1
def context_names_from_params
-
332
return [] unless (name_list = Card::Env.slot_opts.delete(:name_context))
-
-
name_list.to_s.split(",").map(&:to_name)
-
end
-
-
1
def context_names_to_params
-
return if context_names.empty?
-
-
context_names.join(",")
-
end
-
-
1
def add_name_context name=nil
-
322
name ||= card.name
-
322
@context_names = (context_names + name.to_name.part_names).uniq
-
end
-
-
1
def title_in_context title=nil
-
1686
keep_safe = title&.html_safe?
-
1686
title = title ? title.to_name.absolute_name(card.name) : card.name
-
1686
newtitle = title.from(*context_names)
-
1686
keep_safe ? newtitle.html_safe : newtitle
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Format
-
1
module Error
-
1
def ok? task
-
7368
task = :create if task == :update && card.new_card?
-
7368
card.ok? task
-
end
-
-
1
def anyone_can? task
-
return false unless task.is_a? Symbol
-
@anyone_can ||= {}
-
@anyone_can[task] = card.anyone_can? task if @anyone_can[task].nil?
-
@anyone_can[task]
-
end
-
-
1
def view_for_unknown _view
-
25
if main?
-
root.error_status = 404
-
:not_found
-
else
-
25
:unknown
-
end
-
end
-
-
1
def view_for_denial view, task
-
527
@denied_task = task
-
527
root.error_status = 403 if focal? && voo.root?
-
527
view_setting(:denial, view) || :denial
-
end
-
-
1
def monitor_depth
-
21848
raise Card::Error::UserError, tr(:too_deep) if depth >= Card.config.max_depth
-
21848
yield
-
end
-
-
1
def rescue_view e, view
-
method = loud_error? ? :loud_error : :quiet_error
-
send method, e, view
-
end
-
-
1
def error_cardname _exception
-
if card&.name.present?
-
safe_name
-
else
-
I18n.t :no_cardname, scope: %i[lib card format error]
-
end
-
end
-
-
1
def loud_error?
-
focal? || Card.config.raise_all_rendering_errors
-
end
-
-
1
def loud_error e, view
-
log_error e if focal? && voo.root?
-
card.errors.add "#{view} view", rendering_error(e, view) if card.errors.empty?
-
raise e
-
end
-
-
1
def quiet_error e, view
-
# TODO: unify with Card::Error#report
-
log_error e
-
rendering_error e, view
-
end
-
-
1
def log_error e
-
Rails.logger.info e.message
-
Rails.logger.debug e.backtrace.join("\n")
-
end
-
-
1
def rendering_error exception, view
-
if exception.is_a? Card::Error::UserError
-
exception.message
-
else
-
tr :error_rendering, scope: %i[lib card format error],
-
cardname: error_cardname(exception), view: view
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Format
-
1
module MethodDelegation
-
RENDER_METHOD_RE =
-
1
/^
-
(?<underscore>_)? # leading underscore to skip permission check
-
render
-
(?:_(?<view>\w+))? # view name
-
(?<bang>!)? # trailing bang to skip optional check
-
$/x
-
-
1
def api_render match, opts
-
# view can be part of method name or first argument
-
22880
view = match[:view] || opts.shift
-
22880
render! view, render_args(match[:underscore], match[:bang], opts)
-
end
-
-
1
def action_view
-
193993
@action_view ||= root? ? new_action_view : root.action_view
-
end
-
-
1
private
-
-
1
def api_render? method
-
162312
method.match RENDER_METHOD_RE
-
end
-
-
1
def respond_to_missing? method, _include_private=false
-
90643
api_render?(method) || action_view?(method)
-
end
-
-
1
def action_view? method
-
90621
action_view.respond_to? method
-
end
-
-
# TODO: make it so we fall back to super if action_view can't handle method.
-
# It's not as easy as `elsif api_render? method`, because respond_to gives
-
# false for many methods action view can actually handle, like `h`
-
1
def method_missing method, *opts, &proc
-
71669
if (match = api_render? method)
-
22880
api_render match, opts
-
else
-
66718
delegate_to_action_view(method, opts, proc) { yield }
-
end
-
end
-
-
1
def render_args underscore, bang, opts
-
# opts is a list; args is a hash. we're using various inputs to build the hash
-
22880
(opts[0] ? opts.shift.clone : {}).tap do |args|
-
22880
args[:optional] = (opts.shift || args[:optional] || :show) unless bang
-
22880
args[:skip_perms] = true if underscore
-
end
-
end
-
-
# TODO: review this. it's quite old, and there might be a better way to do this now.
-
1
def new_action_view
-
395
c = controller
-
395
lookup_context = ActionView::LookupContext.new c.class.view_paths
-
395
ActionView::Base.new(lookup_context, { _routes: c._routes }, c).tap do |t|
-
395
t.extend c.class._helpers
-
end
-
end
-
-
1
def delegate_to_action_view method, opts, proc
-
66718
proc = proc { |*a| raw yield(*a) } if proc
-
48789
response = action_view.send method, *opts, &proc
-
48789
response.is_a?(String) ? action_view.raw(response) : response
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Format
-
# processing nests
-
1
class Nest
-
1
include Fetch
-
-
1
attr_accessor :format, :card, :view, :view_opts, :format_opts
-
1
def initialize format, cardish, view_opts={}, format_opts={}
-
6140
@format = format
-
6140
@view_opts = view_opts
-
6140
@format_opts = format_opts.clone
-
6140
@override = @format_opts.delete(:override) != false
-
6140
@card ||= fetch_card cardish
-
# note: fetch_card can alter view and view_opts[:nest_name]
-
end
-
-
1
def prepare
-
6140
prepare_view_and_opts!
-
6140
subformat = prepare_subformat
-
6140
@view = subformat.modal_nest_view @view if @override
-
6140
yield subformat, view
-
end
-
-
1
private
-
-
1
def prepare_view_and_opts!
-
6140
view_opts[:nest_name] ||= card.name
-
6140
@view ||= prepare_view
-
# TODO: handle in closed / edit view definitions
-
6140
view_opts[:home_view] ||= %i[closed edit].member?(view) ? :open : view
-
end
-
-
1
def prepare_view
-
6140
view = view_opts[:view] || format.implicit_nest_view
-
# TODO: canonicalize view and modal_nest_view handling should be in Card::View,
-
# not here. (Make sure processing only happens on nests/root views)
-
6140
Card::View.normalize view
-
end
-
-
# @return [Format] subformat object
-
1
def prepare_subformat
-
6140
return format if reuse_format?
-
-
6126
sub = format.subformat card, format_opts
-
6126
sub.main! if view_opts[:main]
-
6126
sub
-
end
-
-
# sometimes we use the same format (rather than a new subformat)
-
# when nesting the same card. this reduces overhead and optimizes
-
# caching
-
1
def reuse_format?
-
6140
self_nest? && !nest_recursion_risk?
-
end
-
-
1
def self_nest?
-
6140
self_nest = view_opts[:nest_name] =~ /^_(self)?$/ &&
-
format.context_card == format.card
-
-
# self nest in focal format should add depth (to catch recursions) but
-
# remain focal
-
6140
format_opts[:focal] = true if self_nest && format.focal?
-
6140
self_nest
-
end
-
-
# don't reuse the format when there is a risk of recursion, because while nest
-
# recursion is caught, view recursion is not.
-
# TODO: catch view recursion and remove this. (Should be straightforward within voo)
-
1
def nest_recursion_risk?
-
14
content_view? || format.voo&.structure
-
end
-
-
1
def content_view?
-
# TODO: this should be specified in view definition
-
14
%i[
-
bar expanded_bar core content titled open closed open_content one_line_content
-
].member? @view.to_sym
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Format
-
1
class Nest
-
# Fetch card for a nest
-
1
module Fetch
-
1
private
-
-
1
def fetch_card cardish
-
6140
case cardish
-
796
when Card then cardish
-
448
when Symbol, Integer then Card.fetch cardish
-
300
when "_", "_self" then format.context_card
-
4596
else new_card cardish
-
end
-
rescue Card::Error::CodenameNotFound
-
not_found_codename cardish
-
end
-
-
1
def not_found_codename cardish
-
@view = :not_found
-
c = Card.new name: Array.wrap(cardish).join(Card::Name.joint).to_s
-
c.errors.add :codename, not_found_codename_error(cardish)
-
c
-
end
-
-
1
def not_found_codename_error codename
-
::I18n.t :exception_unknown_codename, codename: codename,
-
scope: "lib.card.codename"
-
end
-
-
1
def new_card cardish
-
4596
view_opts[:nest_name] = Card::Name[cardish].to_s
-
4596
Card.fetch cardish, new: new_card_args
-
end
-
-
1
def new_card_args
-
4596
args = { name: view_opts[:nest_name] }
-
4596
args[:type] = view_opts[:type] if view_opts[:type]
-
4596
args.merge(new_supercard_args)
-
.merge(new_main_args)
-
.merge(new_content_args)
-
end
-
-
1
def new_supercard_args
-
# special case. gets absolutized incorrectly. fix in name?
-
4596
return {} if view_opts[:nest_name].strip.blank?
-
4596
{ supercard: format.context_card }
-
end
-
-
1
def new_main_args
-
4596
nest_name = view_opts[:nest_name]
-
4596
return {} unless nest_name =~ /main/
-
224
{ name: nest_name.gsub(/^_main\+/, "+"),
-
supercard: format.root.card }
-
end
-
-
1
def new_content_args
-
4596
content = content_from_shorthand_param || content_from_subcard_params
-
4596
content ? { content: content } : {}
-
end
-
-
1
def content_from_shorthand_param
-
# FIXME: this is a lame shorthand; could be another card's key
-
# should be more robust and managed by Card::Name
-
4596
shorthand_param = view_opts[:nest_name].tr "+", "_"
-
4596
Env.params[shorthand_param]
-
end
-
-
1
def content_from_subcard_params
-
4596
Env.params.dig "subcards", view_opts[:nest_name], "content"
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Format
-
# the core of the nesting api
-
1
module Nesting
-
1
include Main
-
1
include Subformat
-
1
include Mode
-
-
# @param cardish card mark
-
# @param view_opts [Hash] {Card::View::Options view options}, passed on to render.
-
# @param format_opts [Hash] opts will be passed on to subformat
-
1
def nest cardish, view_opts={}, format_opts={}
-
6140
return "" if nest_invisible?
-
-
6140
nest = Card::Format::Nest.new self, cardish, view_opts, format_opts
-
6140
nest.prepare do |subformat, view|
-
12280
rendered = count_chars { subformat.render view, view_opts }
-
6140
block_given? ? yield(rendered, view) : rendered
-
end
-
end
-
-
# Shortcut for nesting field cards
-
# @example
-
# home = Card['home'].format
-
# home.nest :self # => nest for '*self'
-
# home.field_nest :self # => nest for 'Home+*self'
-
1
def field_nest field, opts={}
-
6
fullname = card.name.field(field) unless field.is_a? Card
-
6
opts[:title] ||= Card.fetch_name(field).vary("capitalized")
-
6
nest fullname, opts
-
end
-
-
# create a path for a nest with respect to the nest options
-
1
def nest_path name, nest_opts={}
-
path_opts = { slot: nest_opts.clone, mark: name }
-
path_opts[:view] = path_opts[:slot].delete :view
-
path path_opts
-
end
-
-
# view used if unspecified in nest.
-
# frequently overridden in other formats
-
1
def default_nest_view
-
:name
-
end
-
-
1
def implicit_nest_view
-
143
voo_items_view || default_nest_view
-
end
-
-
1
private
-
-
1
def nest_invisible?
-
6140
nest_mode == :compact && @char_count && (@char_count > max_char_count)
-
end
-
-
1
def count_chars
-
6140
result = yield
-
6140
return result unless nest_mode == :compact && result
-
-
@char_count ||= 0
-
@char_count += result.length
-
result
-
end
-
-
1
def max_depth
-
Card.config.max_depth
-
end
-
-
1
def max_char_count
-
Card.config.max_char_count
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Format
-
1
module Nesting
-
# Handle the main nest
-
1
module Main
-
1
def wrap_main
-
yield # no wrapping in base format
-
end
-
-
1
def main_nest opts
-
224
wrap_main do
-
224
main.rendered || main_nest_render(opts)
-
end
-
end
-
-
1
def main_nest_render opts={}
-
with_nest_mode :normal do
-
if block_given?
-
block.call
-
else
-
nest root.card, opts.merge(main_view: true, main: true)
-
end
-
end
-
end
-
-
1
def main_nest? nest_name
-
3646
nest_name == "_main" # && !root.already_mained?
-
end
-
-
1
def already_mained?
-
return true if @main || @already_main
-
-
@already_main = true
-
false
-
end
-
-
1
def main!
-
224
@main = true
-
end
-
-
# view=edit&items=closed
-
1
def main_nest_options
-
258
inherit(:main_opts) || {}
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Format
-
1
module Nesting
-
# Nest modes are states that can alter a nest's view
-
1
module Mode
-
# A nest can be rendered in one of four modes: normal, compact, edit, or template.
-
-
# In _normal_ mode nests are rendered in the requested view without alteration.
-
# In _compact_ mode nest rendering is altered to fit within a single line
-
# In _edit_ mode, a nest's view is replaced (where applicable) with a form
-
# element to edit content
-
# In _template_ mode, the view is replaced with a link to a nest editor to edit
-
# rules and options for that nest
-
-
# current nest mode
-
# @return [Symbol] :normal, :compact, :edit, or :template
-
1
def nest_mode
-
25924
@nest_mode ||= parent ? parent.nest_mode : :normal
-
end
-
-
# run block with new_mode as nest_mode, then return to prior mode
-
# @param new_mode [Symbol] :normal, :compact, :edit, or :template
-
# @return block result
-
1
def with_nest_mode new_mode, &block
-
2158
if new_mode == @nest_mode
-
1257
yield
-
else
-
901
with_altered_nest_mode new_mode, &block
-
end
-
end
-
-
1
def with_altered_nest_mode new_mode
-
901
old_mode = nest_mode
-
901
@nest_mode = new_mode
-
901
yield
-
ensure
-
901
@nest_mode = old_mode
-
end
-
-
# view to be rendered in current mode
-
# @param view [Symbol]
-
# @return [Symbol ] viewname
-
1
def modal_nest_view view
-
# Note: the subformat always has the same nest_mode as its parent format
-
6140
case nest_mode
-
214
when :edit then view_in_edit_mode(view)
-
21
when :template then :template_nest
-
when :compact then view_in_compact_mode(view)
-
5905
else view
-
end
-
end
-
-
# Returns the view that the card should use when nested in edit mode
-
# @param view [Symbol]
-
# @return [Symbol] viewname
-
1
def view_in_edit_mode view
-
214
hide_view_in_edit_mode?(view) ? :blank : :edit_in_form
-
end
-
-
# @param view [Symbol]
-
# @return [True/False]
-
1
def hide_view_in_edit_mode? view
-
214
view_setting(:perms, view) == :none || # view never edited
-
card.structure || # not yet nesting structures
-
card.key.blank? # eg {{_self|type}} on new cards
-
end
-
-
# the view that should be used when nested in compact mode
-
# @param view [Symbol]
-
# @return [Symbol] viewname
-
1
def view_in_compact_mode view
-
configured_view_in_compact_mode(view) ||
-
(card.known? ? :one_line_content : :compact_missing)
-
end
-
-
# the view configured in view definition for use when nested in compact mode
-
# @param view [Symbol]
-
# @return [Symbol] viewname
-
1
def configured_view_in_compact_mode view
-
compact_config = view_setting(:compact, view)
-
return view if compact_config == true
-
-
compact_config
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Format
-
1
module Nesting
-
# Formats can have subformats. Lineage can be retraced through "parent" format.
-
1
module Subformat
-
# note: while it is possible to have a subformat of a different class,
-
# the :format_class value takes precedence over :format.
-
1
def subformat subcard, opts={}
-
6208
subcard = subformat_card subcard
-
6208
opts = opts.merge(parent: self).reverse_merge(format_class: self.class)
-
6208
self.class.new subcard, opts
-
end
-
-
1
def root
-
13544
@root ||= parent ? parent.root : self
-
end
-
-
1
def main
-
224
if main?
-
224
self
-
elsif !root?
-
parent.main
-
end
-
end
-
-
1
def depth
-
35016
@depth ||= parent ? (parent.depth + 1) : 0
-
end
-
-
1
def root?
-
6603
depth.zero?
-
end
-
-
# is format's card the format of the main card on a page?
-
1
def main?
-
350
depth.zero?
-
end
-
-
# is format's card the format of the requested card?
-
# (can be different from main in certain formats, see override in HtmlFormat)
-
1
def focal?
-
@focal ||= depth.zero?
-
end
-
-
1
def field_subformat field
-
field = card.name.field(field) unless field.is_a?(Card)
-
subformat field
-
end
-
-
1
def inherit variable
-
2524
variable = "@#{variable}" unless variable.to_s.start_with? "@"
-
2524
instance_variable_get(variable) || parent&.inherit(variable)
-
end
-
-
1
private
-
-
1
def subformat_card subcard
-
6208
return subcard if subcard.is_a? Card
-
Card.fetch subcard, new: {}
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Format
-
1
module Registration
-
1
def register format
-
9
registered << format.to_s
-
end
-
-
1
def new card, opts={}
-
7806
if self != Format
-
7007
super
-
else
-
799
klass = format_class opts
-
799
self == klass ? super : klass.new(card, opts)
-
end
-
end
-
-
1
def format_class opts
-
799
return opts[:format_class] if opts[:format_class]
-
-
799
format = opts[:format] || :html
-
799
class_from_name format_class_name(format)
-
end
-
-
1
def format_class_name format
-
1681
format = format.to_s
-
1681
format = "" if format == "base"
-
1681
format = aliases[format] if aliases[format]
-
1681
"#{format.camelize}Format"
-
end
-
-
1
def format_sym format
-
514
return format if format.is_a? Symbol
-
-
257
match = format.to_s.match(/::(?<format>[^:]+)Format/)
-
257
match ? match[:format].underscore.to_sym : :base
-
end
-
-
1
def class_from_name formatname
-
1053
if formatname == "Format"
-
51
Card::Format
-
else
-
1002
Card::Format.const_get formatname
-
end
-
end
-
-
1
def format_ancestry
-
14456
ancestry = [self]
-
14456
ancestry += superclass.format_ancestry unless self == Card::Format
-
14456
ancestry
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Format
-
# View rendering methods.
-
#
-
1
module Render
-
# view=open&layout=simple
-
1
def render! view, view_options={}
-
23354
voo = View.new self, view, view_options, @voo
-
23354
with_voo voo do
-
23354
voo.process do |final_view|
-
21848
final_render final_view
-
end
-
end
-
rescue StandardError => e
-
rescue_view e, view
-
end
-
-
1
def with_voo voo
-
23354
old_voo = @voo
-
23354
@voo = voo
-
23354
yield
-
ensure
-
23354
@voo = old_voo
-
end
-
-
1
def before_view view
-
23354
try "_before_#{view}"
-
end
-
-
1
def voo
-
92385
@voo ||= View.new self, nil, {}
-
end
-
-
1
def show_view? view, default_viz=:show
-
2538
voo.process_visibility # trigger viz processing
-
2538
visibility = voo.viz_hash[view] || default_viz
-
2538
visibility == :show
-
end
-
-
1
def final_render view
-
21848
current_view(view) do
-
21848
with_wrapper do
-
21848
method = view_method view
-
21848
rendered = final_render_call method
-
21848
add_debug_info view, method, rendered
-
end
-
end
-
end
-
-
1
def final_render_call method
-
21848
method.call
-
end
-
-
1
def add_debug_info view, method, rendered
-
21848
return rendered unless show_debug_info?
-
-
<<-HTML
-
<view-debug view='#{safe_name}##{view}' src='#{pretty_path method.source_location}' module='#{method.owner}'/>
-
#{rendered}
-
HTML
-
end
-
-
1
def show_debug_info?
-
21848
Env.params[:debug] == "view"
-
end
-
-
1
def pretty_path source_location
-
source_location.first.gsub(%r{^.+mod\d+-([^/]+)}, '\1: ') + ":" +
-
source_location.second.to_s
-
end
-
-
# :standard, :always, :never
-
1
def view_cache_setting view
-
voo&.cache || view_setting(:cache, view) || :standard
-
end
-
-
1
def view_setting setting_name, view
-
64090
try Card::Set::Format.view_setting_method_name(view, setting_name)
-
end
-
-
1
def stub_render cached_content
-
stub_debugging do
-
expand_stubs cached_content
-
end
-
end
-
-
1
def stub_debugging
-
result = yield
-
if Rails.env.development? && result =~ /stub/
-
Rails.logger.debug "STUB IN RENDERED VIEW: #{card.name}: " \
-
"#{voo.ok_view}\n#{result}"
-
end
-
result
-
end
-
-
1
def prepare_stub_nest stub_hash
-
stub_card = Card.fetch_from_cast stub_hash[:cast]
-
view_opts = stub_hash[:view_opts]
-
voo.normalize_special_options! view_opts
-
if stub_card&.key.present? && stub_card.key == card.key
-
view_opts[:nest_name] ||= "_self"
-
end
-
yield stub_card, view_opts
-
end
-
-
1
def expand_stubs cached_content
-
return cached_content unless cached_content.is_a? String
-
-
conto = Card::Content.new cached_content, self, chunk_list: :stub
-
conto.process_chunks
-
-
if conto.pieces.size == 1
-
# for stubs in json format this converts a single stub back
-
# to it's original type (e.g. a hash)
-
conto.pieces.first.to_s
-
else
-
conto.to_s
-
end
-
end
-
-
1
def view_method view
-
21848
unless supports_view? view
-
raise Card::Error::UserError, unsupported_view_error_message(view)
-
end
-
-
21848
method Card::Set::Format.view_method_name(view)
-
end
-
-
1
def supports_view? view
-
21848
respond_to? Card::Set::Format.view_method_name(view)
-
end
-
-
1
def current_view view
-
21848
old_view = @current_view
-
21848
@current_view = view
-
21848
yield
-
ensure
-
21848
@current_view = old_view
-
end
-
-
1
def stub_nest stub_hash
-
prepare_stub_nest(stub_hash) do |stub_card, view_opts|
-
nest stub_card, view_opts, stub_hash[:format_opts]
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Format
-
1
module Wrapper
-
1
def with_wrapper
-
21848
if voo.layout.present?
-
224
voo.wrap ||= []
-
224
layout = voo.layout.to_name.key
-
# don't wrap twice with modals or overlays
-
# this can happen if the view is wrapped with modal
-
# and is requested with layout=modal param
-
224
voo.wrap.unshift layout unless voo.wrap.include? layout.to_sym
-
end
-
-
21848
@rendered = yield
-
21848
wrap_with_wrapper
-
end
-
-
1
def wrap_with_wrapper
-
21848
return @rendered unless voo.wrap.present?
-
-
852
voo.wrap.reverse.each do |wrapper, opts|
-
@rendered =
-
852
render_with_wrapper(wrapper, opts) ||
-
render_with_card_layout(wrapper) ||
-
raise_wrap_error(wrapper)
-
end
-
852
@rendered
-
end
-
-
1
def render_with_wrapper wrapper, opts
-
1480
try("wrap_with_#{wrapper}", opts) { @rendered }
-
end
-
-
1
def render_with_card_layout mark
-
224
return unless Card::Layout.card_layout? mark
-
-
224
Card::Layout::CardLayout.new(mark, self).render
-
end
-
-
1
def raise_wrap_error wrapper
-
if wrapper.is_a? String
-
raise Card::Error::UserError, "unknown layout card: #{wrapper}"
-
else
-
raise ArgumentError, "unknown wrapper: #{wrapper}"
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
# Translates names to ids and vice versa via a cached "lex" representation:
-
# name for simple cards, [left_id, right_id] for compound cards.
-
#
-
# Note, unlike Card::Fetch, Card::Lexicon:
-
# 1. does NOT distinguish between trashed and untrashed cards.
-
# 2. does NOT respect local name changes
-
1
module Lexicon
-
1
class << self
-
# param id [Integer]
-
# @return [String]
-
1
def name id
-
57040
return unless id.present?
-
-
57040
name = (lex = id_to_lex id) && lex_to_name(lex)
-
57040
(name || "").to_name
-
end
-
-
# param name [String]
-
# @return [Integer]
-
1
def id name
-
24316
return unless name.present?
-
-
24216
(lex = name_to_lex name.to_name) && lex_to_id(lex)
-
end
-
-
1
def cache
-
80707
Card::Cache[Lexicon]
-
end
-
-
1
def add card
-
134
lex = card.lex
-
134
cache.write card.id.to_s, lex
-
134
cache.write cache_key(lex), card.id
-
end
-
-
1
def update card
-
add card
-
cache.delete cache_key(card.old_lex)
-
end
-
-
# def delete card
-
# cache.delete card.id.to_s
-
# cache.delete cache_key(card.old_lex)
-
# end
-
-
1
def lex_to_name lex
-
59015
return lex unless lex&.is_a? Array
-
40236
lex.map { |side_id| name side_id or return }.join(Card::Name.joint).to_name
-
end
-
-
1
private
-
-
1
def id_to_lex id
-
57040
cache.fetch id.to_s do
-
4230
result = Card.where(id: id).pluck(:name, :left_id, :right_id).first
-
4230
return unless result
-
-
4230
result[0] || [result[1], result[2]]
-
end
-
end
-
-
1
def name_to_lex name
-
24216
if name.simple?
-
17594
name
-
6622
elsif (left_id = id name.left_name) && (right_id = id name.right_name)
-
5805
[left_id, right_id]
-
end
-
end
-
-
1
def lex_to_id lex
-
23399
cache.fetch cache_key(lex) do
-
4238
Card.where(lex_query(lex)).pluck(:id).first
-
end
-
end
-
-
1
def lex_query lex
-
4238
if lex.is_a?(Array)
-
2497
{ left_id: lex.first, right_id: lex.last }
-
else
-
1741
{ key: lex.to_name.key }
-
end
-
end
-
-
1
def cache_key lex
-
23533
"L-" + (lex.is_a?(Array) ? lex.join("-") : lex.to_name.key)
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
require "open-uri"
-
-
1
class Card
-
1
class Mailer < ActionMailer::Base
-
1
@@defaults = Card.config.email_defaults || {}
-
1
@@defaults.symbolize_keys!
-
1
@@defaults[:return_path] ||= @@defaults[:from] if @@defaults[:from]
-
1
@@defaults[:charset] ||= "utf-8"
-
1
default @@defaults
-
-
1
class << self
-
1
def new_mail *args, &block
-
21
mail = Mail.new(args, &block)
-
21
method = Card::Mailer.delivery_method
-
21
mail.delivery_method(method, Card::Mailer.send(:"#{method}_settings"))
-
21
mail.perform_deliveries = Card::Mailer.perform_deliveries
-
21
mail.raise_delivery_errors = Card::Mailer.raise_delivery_errors
-
21
mail
-
end
-
-
1
def layout message
-
20
<<-HTML
-
<!DOCTYPE html>
-
<html>
-
<head>
-
<meta http-equiv="Content-type" content="text/html;charset=UTF-8"/>
-
</head>
-
<body>
-
#{message}
-
</body>
-
</html>
-
HTML
-
end
-
end
-
end
-
end
-
1
class Card
-
# Card::Mark provides class methods for Card to translate all different kind
-
# of card identifiers to card objects.
-
1
module Mark
-
1
ID_MARK_RE = /^~(?<id>\d+)$/.freeze
-
1
CODENAME_MARK_RE = /^:(?<codename>\w+)$/.freeze
-
-
# translates marks (and other inputs) into a Card
-
#
-
# @param cardish [Card, Card::Name, String, Symbol, Integer]
-
# @return Card
-
1
def cardish cardish
-
if cardish.is_a? Card
-
cardish
-
else
-
fetch cardish, new: {}
-
end
-
end
-
-
# translates various inputs into either an id or a name.
-
# @param parts [Array<Symbol, Integer, String, Card::Name, Card>] a mark or mark parts
-
# @return [Integer or Card::Name]
-
1
def id_or_name parts
-
219438
mark = parts.flatten
-
219438
mark = mark.first if mark.size <= 1
-
219438
id_from_mark(mark) || name_from_mark(mark)
-
end
-
-
1
def id_from_mark mark
-
219438
case mark
-
21188
when Integer then mark
-
8777
when Symbol then Card::Codename.id! mark
-
156900
when String then id_from_string mark
-
when Card then mark.id
-
end
-
end
-
-
# translates string identifiers into an id:
-
# - string id notation (eg "~75")
-
# - string codename notation (eg ":options")
-
#
-
# @param mark [String]
-
# @return [Integer or nil]
-
1
def id_from_string mark
-
660567
case mark
-
26
when ID_MARK_RE then Regexp.last_match[:id].to_i
-
112
when CODENAME_MARK_RE then Card::Codename.id! Regexp.last_match[:codename]
-
end
-
end
-
-
1
def bad_mark mark
-
case mark
-
when ID_MARK_RE
-
raise Card::Error::NotFound, "id doesn't exist: #{Regexp.last_match[:id]}"
-
when CODENAME_MARK_RE
-
raise Card::Error::CodenameNotFound,
-
"codename doesn't exist: #{Regexp.last_match[:codename]}"
-
else
-
raise Card::Error, "invalid mark: #{mark}"
-
end
-
end
-
-
1
def name_from_mark mark
-
189345
Card::Name[mark]
-
end
-
end
-
end
-
1
class Card
-
# A Card Mod (short for "module" or "modification") is a discrete piece of Decko
-
# functionality. Mods are how the Decko community develops and shares code.
-
# If you want to customize a deck in a way that can't be done on the site itself,
-
# try a mod.
-
#
-
# The simplest way to add a mod is to run this command in your deck:
-
#
-
# decko generate card:mod MOD_NAME
-
#
-
# This will create the following directories:
-
#
-
# DECK_NAME/mod/MOD_NAME
-
# DECK_NAME/mod/MOD_NAME/lib
-
# DECK_NAME/mod/MOD_NAME/public
-
# DECK_NAME/mod/MOD_NAME/set
-
#
-
# The lib directory contains libraries, of course. And files in the public directory
-
# are public and served directly.
-
#
-
# But in most mods, the focal point is the *set* directory.
-
#
-
# ## Set Modules
-
#
-
# Set modules define methods for a given set of cards and their format objects.
-
# They are defined in a mod's _set_ directory. For example, suppose you've created a
-
# mod that called *biz*, your deck has Company cards, and you want to extend the
-
# behavior of those cards.
-
#
-
# You can add a set module like so:
-
#
-
# decko generate set biz type company
-
#
-
# This will create the following two files:
-
#
-
# mod/biz/set/type/company.rb
-
# mod/biz/spec/set/type/company.rb
-
#
-
# If you would like to break this code into smaller files, you can extend this
-
# pattern into another directory, eg:
-
#
-
# mod/biz/set/type/company/foo.rb
-
# mod/biz/set/type/company/bar.rb
-
#
-
# The general pattern can be expressed as follows:
-
#
-
# DECKNAME/mod/MODNAME/set/SET_PATTERN/ANCHOR[/FREENAME].rb
-
#
-
# Learn more:
-
# - {Card} introduces card objects
-
# - {Card::Set} provides an overview of how set modules work
-
# - {Card::Set::Format} explains the basics of the format API
-
# - {Card::Set::Format::AbstractFormat} explains the basics of the view definition API
-
# - {Card::Set::Event:Api} explains the basics of the event API
-
#
-
# ## Other Directories
-
#
-
# Other ways your mod can extend Decko functionality include:
-
# - **format** for creating new formats (think file extensions)
-
# - **set_pattern** for additional {Card::Set::Pattern set patterns},
-
# or types of sets.
-
# - **chunk** provides tools for finding new patterns in card content
-
# - **file** for fixed initial card content
-
1
module Mod
-
1
class << self
-
1
def load
-
1
return if ENV["CARD_MODS"] == "none"
-
-
1
if Card.take
-
1
Card::Mod::Loader.load_mods
-
else
-
Rails.logger.warn "empty database"
-
end
-
end
-
-
# @return an array of Rails::Path objects
-
1
def dirs
-
2160
@dirs ||= Mod::Dirs.new(Card.paths["mod"].existent)
-
end
-
-
1
def dependencies name, nickname=true
-
16
return unless (spec = gem_spec name, nickname)
-
8
deps = spec&.dependencies || []
-
19
dep_names = deps.map { |dep| dependencies dep.name, false }
-
8
(dep_names << spec).flatten.compact.uniq
-
end
-
-
1
def gem_spec name, nickname=true
-
16
name = "card-mod-#{name}" if nickname && !name.match?(/^card-mod/)
-
16
spec = Gem::Specification.stubs_for(name)&.first
-
16
Cardio.gem_mod_spec?(spec) ? spec : nil
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class << self
-
1
def config
-
58926
Cardio.config
-
end
-
-
1
def paths
-
627
Cardio.paths
-
end
-
end
-
-
1
module Mod
-
# Dirs objects are used to manage the load paths for card mods.
-
# Mods can be loaded as gems and by using directories with mod subdirectories.
-
#
-
# 1. Gemfile
-
# A mod gem needs a metadata attribute with { "card-mod" => "the_mod_name" }
-
# or the name has to start with "card-mod-".
-
# Then you can just add it to your Gemfile. Otherwise it won't be recognized as mod.
-
#
-
# 2. mod directory
-
# Give a path to a directory with mods. The mods will be loaded in alphabetical order.
-
# To change the load order you can add number prefixes to the mod names
-
# (like "01_this_first") or add a Modfile.
-
# In the Modfile you list all the mods you want to be loaded from that directory
-
# in load order with a preceding "mod" command (similar to a Gemfile).
-
# The mods are expected in subdirectories with the mod names.
-
#
-
# Mods in Modfiles are always loaded before mods in the Gemfile.
-
# If you have to change the order add gem mods to your Modfile using the
-
# mod_gem command. You can omit the 'card-mod' prefix.
-
#
-
# Example for a mod directory:
-
# # my_mod/Modfile
-
# mod "twitter"
-
# gem_mod "logger"
-
# mod "cache"
-
#
-
# # directory structure
-
# my_mods/
-
# Modfile
-
# cache/
-
# set/
-
# all/
-
# my_cache.rb
-
# twitter/
-
# set/
-
# type/
-
# basic.rb
-
# set_pattern/
-
# my_pattern.rb
-
#
-
# Dir checks always for gems. You can initialize an Dirs object with an additional
-
# array of paths to card mod directories.
-
1
class Dirs < Array
-
1
attr_reader :mods
-
-
# @param mod_paths [String, Array<String>] paths to directories that contain mods
-
1
def initialize mod_paths=[]
-
1
@mods = []
-
1
@loaded_gem_mods = ::Set.new
-
1
@paths = {}
-
1
mod_paths = Array(mod_paths)
-
1
mod_paths.each do |mp|
-
2
@current_path = mp
-
2
load_from_modfile || load_from_dir
-
end
-
1
load_from_gemfile
-
1
super()
-
1
@mods.each do |mod_name|
-
31
self << @paths[mod_name]
-
end
-
end
-
-
# Add a mod to mod load paths
-
1
def add_path mod_name, path=nil
-
31
if @mods.include? mod_name
-
raise Error,
-
"name conflict: mod with name \"#{mod_name}\" already loaded"
-
end
-
31
@mods << mod_name
-
31
path ||= File.join @current_path, mod_name
-
31
@paths[mod_name] = path
-
end
-
-
1
def gem_mod name
-
5
deps = Mod.dependencies name
-
5
unknown_gem_mod!(name) if deps.blank?
-
13
deps.each { |spec| add_gem_mod spec.name, spec.full_gem_path }
-
end
-
-
1
def unknown_gem_mod! name
-
raise Error, "Unknown gem \"#{name}\". Make sure it is in your Gemfile."
-
end
-
-
1
def add_gem_mod mod_name, mod_path
-
32
return if @loaded_gem_mods.include?(mod_name)
-
-
24
@loaded_gem_mods << mod_name
-
24
add_path mod_name, mod_path
-
end
-
-
1
alias_method :mod, :add_path
-
-
# @param mod_name [String] the name of a mod
-
# @return the path to mod `mod_name`
-
1
def path mod_name
-
2153
@paths[mod_name] || @paths["card-mod-#{mod_name}"]
-
end
-
-
# Iterate over each mod directory
-
# @param type [Symbol] the type of modification like set, set_pattern, or format.
-
# It is attached as subdirectory.
-
1
def each type=nil
-
6
super() do |path|
-
186
dirname = type ? File.join(path, type.to_s) : path
-
186
next unless Dir.exist? dirname
-
-
13
yield dirname
-
end
-
end
-
-
1
def each_tmp type
-
1
@mods.each do |mod|
-
31
path = tmp_dir mod, type
-
31
next unless Dir.exist? path
-
-
28
yield path
-
end
-
end
-
-
1
def each_with_tmp type=nil
-
1
@mods.each do |mod|
-
31
dirname = type ? File.join(@paths[mod], type.to_s) : @paths[mod]
-
31
next unless Dir.exist? dirname
-
-
28
yield dirname, tmp_dir(mod, type)
-
end
-
end
-
-
1
def each_assets_path
-
@mods.each do |mod|
-
path = File.join(@paths[mod], "public", "assets")
-
next unless Dir.exist? path
-
-
yield mod, path
-
end
-
end
-
-
1
private
-
-
1
def load_from_modfile
-
2
modfile_path = File.join @current_path, "Modfile"
-
2
return unless File.exist? modfile_path
-
-
1
eval File.read(modfile_path), binding
-
1
true
-
end
-
-
1
def load_from_dir
-
1
Dir.entries(@current_path).sort.each do |filename|
-
3
next if filename =~ /^\./
-
-
add_path filename
-
end.compact
-
end
-
-
1
def load_from_gemfile
-
1
Cardio.gem_mod_specs.each do |mod_name, mod_spec|
-
24
add_gem_mod mod_name, mod_spec.full_gem_path
-
end
-
end
-
-
1
def tmp_dir modname, type
-
59
index = @mods.index modname
-
59
File.join Card.paths["tmp/#{type}"].first,
-
59
"mod#{'%03d' % (index + 1)}-#{modname}"
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Mod
-
# Shared code for the three different load strategies: Eval, TmpFiles and BindingMagic
-
1
class LoadStrategy
-
1
def self.klass symbol
-
case symbol
-
when :tmp_files then TmpFiles
-
when :binding_magic then BindingMagic
-
else Eval
-
end
-
end
-
-
1
def initialize mod_dirs, loader
-
2
@mod_dirs = mod_dirs
-
2
@loader = loader
-
end
-
-
1
def clean_comments?
-
false
-
end
-
-
1
private
-
-
1
def module_type
-
2
@loader.class.module_type
-
end
-
-
1
def module_template
-
438
@loader.class.module_class_template
-
end
-
-
1
def each_file &block
-
1
if module_type == :set
-
each_set_file(&block)
-
else
-
1
each_mod_dir module_type do |base_dir|
-
1
each_file_in_dir base_dir, &block
-
end
-
end
-
end
-
-
1
def each_set_file &block
-
each_mod_dir :set do |base_dir|
-
@loader.patterns.each do |pattern|
-
each_file_in_dir base_dir, pattern.to_s, &block
-
end
-
end
-
end
-
-
1
def each_mod_dir module_type
-
1
@mod_dirs.each module_type do |base_dir|
-
1
yield base_dir
-
end
-
end
-
-
1
def each_file_in_dir base_dir, subdir=nil
-
1
pattern = File.join(*[base_dir, subdir, "**/*.rb"].compact)
-
1
Dir.glob(pattern).sort.each do |abs_path|
-
9
rel_path = abs_path.sub("#{base_dir}/", "")
-
9
const_parts = parts_from_path rel_path
-
9
yield abs_path, const_parts
-
end
-
end
-
-
1
def parts_from_path path
-
# remove file extension and number prefixes
-
438
parts = path.gsub(/\.rb/, "").gsub(%r{(?<=\A|/)\d+_}, "").split(File::SEPARATOR)
-
438
parts.map(&:camelize)
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Mod
-
1
class LoadStrategy
-
# The {TmpFiles} load strategy version for set pattern modules/
-
1
class PatternTmpFiles < TmpFiles
-
1
private
-
-
1
def generate_tmp_files
-
1
prepare_tmp_dir "tmp/set_pattern"
-
1
seq = 100
-
1
each_file do |abs_path, const_parts|
-
9
pattern = const_parts.first.underscore
-
9
to_file = "#{tmp_dir}/#{seq}-#{pattern}.rb"
-
9
write_tmp_file abs_path, to_file, const_parts
-
9
seq += 1
-
end
-
end
-
-
1
def load_tmp_files
-
1
Loader.load_dir tmp_dir
-
end
-
-
1
def tmp_dir
-
10
Card.paths["tmp/set_pattern"].first
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Mod
-
1
class LoadStrategy
-
# The {TmpFiles} load strategy version for set modules
-
1
class SetTmpFiles < LoadStrategy::TmpFiles
-
1
private
-
-
1
def generate_tmp_files
-
1
return unless prepare_tmp_dir "tmp/set"
-
-
1
@mod_dirs.each_with_tmp(:set) do |mod_dir, mod_tmp_dir|
-
28
Dir.mkdir mod_tmp_dir
-
28
Dir.glob("#{mod_dir}/**/*.rb").each do |abs_path|
-
429
rel_path = abs_path.sub "#{mod_dir}/", ""
-
429
tmp_filename = File.join mod_tmp_dir, rel_path
-
429
const_parts = parts_from_path rel_path
-
# puts "write_tmp_file #{abs_path}, #{tmp_filename}, #{const_parts}"
-
429
write_tmp_file abs_path, tmp_filename, const_parts
-
end
-
end
-
end
-
-
1
def load_tmp_files
-
1
@mod_dirs.each_tmp(:set) do |set_tmp_dir|
-
28
Card::Set::Pattern.loadable_codes.each do |pattern|
-
280
pattern_dir = "#{set_tmp_dir}/#{pattern}"
-
280
Loader.load_dir "#{pattern_dir}/**" if Dir.exist? pattern_dir
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Mod
-
1
class LoadStrategy
-
# LoadStrategy for mod modules. It writes the code to tmp files
-
# and then loads the tmp files. (deprecated)
-
1
class TmpFiles < LoadStrategy
-
1
def load_modules
-
2
generate_tmp_files if rewrite_tmp_files?
-
2
load_tmp_files
-
end
-
-
1
def clean_comments?
-
1305
true
-
end
-
-
1
private
-
-
1
def prepare_tmp_dir path
-
2
return unless rewrite_tmp_files?
-
-
2
p = Card.paths[path]
-
2
FileUtils.rm_rf p.first, secure: true if p.existent.first
-
2
Dir.mkdir p.first
-
end
-
-
1
def rewrite_tmp_files?
-
4
if defined?(@rewrite)
-
2
@rewrite
-
else
-
2
@rewrite = !(Rails.env.production? &&
-
Card.paths["tmp/set"].existent.first)
-
end
-
end
-
-
1
def write_tmp_file from_file, to_file, const_parts
-
438
FileUtils.mkdir_p File.dirname(to_file)
-
438
mt = module_template.new const_parts, from_file, self
-
438
File.write to_file, mt.to_s
-
end
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
1
module Mod
-
# Card::Mod::Loader is used to load all part of a mod,
-
# i.e. initializers, patterns, formats, chunks, layouts and sets
-
# cards are not accessible at this point
-
-
# A Loader object provides tools for generating and loading sets and set patterns,
-
# each of which are typically written using a Decko DSL.
-
-
# The mods are given by a Mod::Dirs object.
-
# SetLoader can use three different strategies to load the set modules.
-
1
class Loader
-
1
def initialize load_strategy: nil, mod_dirs: nil
-
2
load_strategy ||= Cardio.config.load_strategy
-
2
mod_dirs ||= Mod.dirs
-
2
klass = load_strategy_class load_strategy
-
2
@load_strategy = klass.new mod_dirs, self
-
end
-
-
1
def load_strategy_class load_strategy
-
case load_strategy
-
when :tmp_files then LoadStrategy::TmpFiles
-
when :binding_magic then LoadStrategy::BindingMagic
-
else LoadStrategy::Eval
-
end
-
end
-
-
1
def load
-
2
@load_strategy.load_modules
-
end
-
-
1
class << self
-
1
attr_reader :module_type
-
-
1
def load_mods
-
1
load_formats
-
1
Card::Mod::Loader::SetPatternLoader.new.load
-
1
Card::Mod::Loader::SetLoader.new.load
-
1
Card::Set.process_base_modules
-
1
load_initializers
-
end
-
-
1
def reload_sets
-
Card::Set::Pattern.reset
-
Card::Set.reset_modules
-
Card::Mod::Loader::SetPatternLoader.new.load
-
Card::Mod::Loader::SetLoader.new(
-
patterns: Card::Set::Pattern.nonbase_loadable_codes
-
).load
-
end
-
-
1
def load_chunks
-
1
Mod.dirs.each(:chunk) do |dir|
-
1
load_dir dir
-
end
-
end
-
-
1
def module_class_template
-
438
const_get :Template
-
end
-
-
# private
-
-
1
def load_initializers
-
1
Card.config.paths["mod/config/initializers"].existent.sort.each do |initializer|
-
2
load initializer
-
end
-
end
-
-
# {Card::Format}
-
1
def load_formats
-
# cheating on load issues now by putting all inherited-from formats in core mod.
-
1
Mod.dirs.each(:format) do |dir|
-
3
load_dir dir
-
end
-
end
-
-
# load all files in directory
-
# @param dir [String] directory name
-
1
def load_dir dir
-
92
Dir["#{dir}/*.rb"].sort.each do |file|
-
# puts Benchmark.measure("from #load_dir: rd: #{file}") {
-
# require file
-
# "require" breaks the reloading in development env
-
458
load file
-
# }.format('%n: %t %r')
-
end
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Mod
-
1
class Loader
-
# A SetLoader object loads all set modules for a list of mods.
-
# The mods are given by a Mod::Dirs object.
-
# SetLoader can use three different strategies to load the set modules.
-
1
class SetLoader < Loader
-
1
@module_type = :set
-
1
attr_accessor :patterns
-
-
1
def initialize args={}
-
1
@patterns = args.delete(:patterns) || Card::Set::Pattern.loadable_codes
-
1
super
-
end
-
-
1
def load_strategy_class load_strategy
-
1
case load_strategy
-
when :tmp_files
-
1
LoadStrategy::SetTmpFiles
-
when :binding_magic
-
LoadStrategy::SetBindingMagic
-
else
-
LoadStrategy::Eval
-
end
-
end
-
-
1
def load
-
1
super
-
# Card::Set.process_base_modules
-
1
Card::Set.clean_empty_modules
-
end
-
-
1
class Template < ModuleTemplate
-
1
def initialize modules, content_path, strategy
-
429
super
-
429
@modules.pop if helper_module?
-
end
-
-
1
def to_const
-
return Object if simple_load?
-
-
@modules.inject(pattern_class) do |const, name_part|
-
const.const_get_or_set name_part do
-
Module.new
-
end
-
end
-
end
-
-
1
def processed_content
-
429
add_explicit_format_modules if @strategy.clean_comments?
-
429
super
-
end
-
-
1
def add_explicit_format_modules
-
429
@content.gsub!(/^ *format +:?(\w+)? *do *$/) do
-
277
format_name = $1.blank? ? nil : $1.to_sym
-
277
"module #{module_name format_name}; " \
-
"module_parent.send :register_set_format, "\
-
"#{format_class format_name}, self; "\
-
"extend Card::Set::AbstractFormat"
-
end
-
end
-
-
1
def module_name format_name
-
508
Card::Format.format_class_name format_name
-
end
-
-
1
def format_class format_name
-
277
klass = ["Card::Format"]
-
277
klass << module_name(format_name) if format_name
-
277
klass.join "::"
-
end
-
-
1
def helper_module?
-
858
if @is_helper_module.nil?
-
852
@is_helper_module = @content =~ /\A#!\s?not? set module/
-
else
-
6
@is_helper_module
-
end
-
end
-
-
# correct line number for error messages
-
1
def offset
-
-5
-
end
-
-
1
private
-
-
1
def submodule_chain
-
951
@modules.map { |m| "module #{m};" }
-
end
-
-
1
def module_chain
-
858
@module_chain ||=
-
["class Card", "module Set", "class #{@pattern.camelize}"] + submodule_chain
-
end
-
-
1
def preamble_bits
-
429
capture_last_module
-
429
[module_chain.join("; "),
-
module_comment,
-
@last_module,
-
set_extension,
-
location_method].compact
-
end
-
-
1
def auto_comment
-
429
"# Set: #{label_body(*@modules)}\n#"
-
end
-
-
1
def label_body *anchors
-
429
if @pattern == "Abstract"
-
58
"Abstract (#{@modules.join ', '})"
-
else
-
371
pattern_label(*anchors)
-
end
-
end
-
-
1
def pattern_label *anchors
-
371
anchor_count = pattern_class.anchor_parts_count
-
371
label = pattern_class.label(pattern_anchor(*anchors, anchor_count))
-
371
remainder = anchors[anchor_count..-1]
-
371
label += " (#{remainder.join ', '})" if remainder.any?
-
371
label
-
end
-
-
1
def pattern_anchor *anchors, anchor_count
-
371
if anchor_count.zero?
-
152
""
-
else
-
219
anchors[0..anchor_count].join(Card::Name.joint)
-
end
-
end
-
-
1
def pattern_class
-
742
@pattern_class ||= Card::Set.const_get_or_set(@pattern.camelize) { Class.new }
-
end
-
-
1
def capture_last_module
-
429
module_chain
-
429
@last_module = @module_chain.pop
-
end
-
-
1
def set_extension
-
429
"extend Card::Set" unless helper_module?
-
end
-
-
1
def location_method
-
429
%(def self.source_location; "#{@content_path}"; end)
-
end
-
-
1
def postamble
-
429
"end;" * (@modules.size + 3)
-
end
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Mod
-
1
class Loader
-
1
class SetPatternLoader < Loader
-
1
@module_type = :set_pattern
-
-
1
def load_strategy_class load_strategy
-
1
case load_strategy
-
when :tmp_files
-
1
LoadStrategy::PatternTmpFiles
-
else # :eval
-
LoadStrategy::Eval
-
end
-
end
-
-
1
class Template < ModuleTemplate
-
1
def to_const
-
return Object if simple_load?
-
-
Card::Set.const_get_or_set(@pattern.camelize) do
-
Class.new(Card::Set::Pattern::Base)
-
end
-
end
-
-
# correct line number for error messages
-
1
def offset
-
-5
-
end
-
-
1
private
-
-
1
def auto_comment
-
9
%(# Set Pattern: #{@pattern.camelize}\n#)
-
end
-
-
1
def module_chain
-
9
"class Card::Set::#{@pattern.camelize} < Card::Set::Pattern::Base"
-
end
-
-
1
def preamble_bits
-
9
[module_comment,
-
module_chain,
-
"extend Card::Set::Pattern::Helper",
-
"cattr_accessor :options",
-
"class << self"]
-
end
-
-
1
def postamble
-
9
<<-RUBY
-
end
-
register "#{@pattern}".underscore.to_sym, (options || {})
-
end
-
RUBY
-
end
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Mod
-
# ModuleTemplate is an abstract class to build ruby modules out of
-
# deckos dsl for sets and set patterns.
-
# {Loader::SetLoader::Template} and {Loader::SetPatternLoader::Template} inherit
-
# from it and adapt the template to their needs.
-
1
class ModuleTemplate
-
1
def initialize modules, content_path, strategy
-
438
modules = Array.wrap modules
-
438
@pattern = modules.shift
-
438
@modules = modules
-
438
@content = ::File.read content_path
-
438
@content_path = content_path
-
438
@strategy = strategy
-
end
-
-
# Evaluates the module in the top level namespace.
-
1
def build
-
eval to_s, TOPLEVEL_BINDING, @content_path, offset
-
end
-
-
# @return [String] the ruby code to build the modal
-
1
def to_s
-
438
simple_load? ? @content : processed_content
-
end
-
-
1
def processed_content
-
438
capture_module_comment if @strategy.clean_comments?
-
438
module_content
-
end
-
-
# Just run the code of the source.
-
# Don't use the path to the file as module hierarchy.
-
1
def simple_load?
-
438
@content =~ /\A#!\s?simple load/
-
end
-
-
1
private
-
-
# find all comment lines at the beginning of a mod file, up to the first
-
# non-comment line. (These will be inserted before the module declaration,
-
# so that Yard will interpret them as a module comment.)
-
1
def capture_module_comment
-
438
content_lines = @content.split "\n"
-
438
comment_lines = []
-
-
438
content_lines.each do |line|
-
554
comment?(line) ? comment_lines << content_lines.shift : break
-
end
-
-
438
@content = content_lines.join "\n"
-
438
@module_comment = comment_lines.join "\n"
-
end
-
-
1
def comment? line
-
554
line.match?(/^ *\#/)
-
end
-
-
# loader template must implement #preamble_bits
-
1
def preamble
-
438
preamble_bits.join "\n"
-
end
-
-
1
def module_comment
-
438
return "" unless @strategy.clean_comments?
-
438
@module_comment = nil if @module_comment.blank?
-
438
[auto_comment, @module_comment].compact.join "\n"
-
end
-
-
1
def module_content
-
# for unknown reasons strip_heredoc doesn't work properly
-
# and with trailing whitespace code like `=begin` fails
-
438
<<~RUBY.strip_heredoc
-
# -*- encoding : utf-8 -*-
-
#{preamble}
-
#{@content}
-
#{postamble}
-
# ~~ generated from #{@content_path} ~~
-
RUBY
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
# require "card/env"
-
-
1
require "cardname"
-
-
1
class Card
-
# The Cardname class provides generalized of Card naming patterns
-
# (compound names, key-based variants, etc)
-
#
-
# Card::Name adds support for deeper card integration
-
1
class Name < Cardname
-
1
include FieldsAndTraits
-
1
include NameVariants
-
-
1
class << self
-
1
def [] *cardish
-
217618
cardish = cardish.first if cardish.size <= 1
-
217618
case cardish
-
1107
when Card then cardish.name
-
5832
when Symbol, Integer then Card.fetch_name(cardish)
-
3925
when Array then smart_compose cardish
-
206754
when String, NilClass then new cardish
-
else
-
raise ArgumentError, "#{cardish.class} not supported as name identifier"
-
end
-
end
-
-
1
def session
-
103
Card::Auth.current.name # also_yuck
-
end
-
-
1
def params
-
224
Card::Env.params
-
end
-
-
1
def new str, validated_parts=nil
-
575795
return compose str if str.is_a?(Array)
-
-
573942
str = str.to_s
-
-
573942
if !validated_parts && str.include?(joint)
-
70275
compose Cardname.split_parts(str)
-
503667
elsif (id = Card.id_from_string(str)) # handles ~ and :
-
10
Card.fetch_name(id) { Card.bad_mark(str) }
-
else
-
503657
super str
-
end
-
end
-
-
# interprets symbols/integers as codenames/ids
-
1
def smart_compose parts
-
11823
name_parts = parts.flatten.map { |part| self[part] }
-
3925
new name_parts.join(joint), true
-
end
-
-
1
def compose parts
-
243520
name_parts = parts.flatten.map { |part| new part }
-
77230
new name_parts.join(joint), true
-
end
-
-
1
def url_key_to_standard key
-
5354
key.to_s.tr "_", " "
-
end
-
end
-
-
1
def star?
-
7674
simple? && s[0, 1] == "*"
-
end
-
-
1
def rstar?
-
7674
right && right[0, 1] == "*"
-
end
-
-
1
def code
-
700
Card::Codename[Card.fetch_id self]
-
end
-
-
1
def setting?
-
Set::Type::Setting.member_names[key]
-
end
-
-
1
def set?
-
Set::Pattern.card_keys[tag_name.key]
-
end
-
end
-
end
-
1
class Card
-
1
class Name
-
# Name-based "Fields" are compound names in which the right name is treated
-
# as an attribute of the left. (Eg MyName+address)
-
#
-
# "Traits" are the subset of fields in which the right name corresponds to a
-
# card with a codename
-
1
module FieldsAndTraits
-
# @return [String]
-
1
def field tag_name
-
214
field_name(tag_name).s
-
end
-
-
# @return [Card::Name]
-
1
def field_name tag_name
-
361
case tag_name
-
when Symbol
-
204
trait_name tag_name
-
else
-
157
tag_name = tag_name.to_s[1..-1] if tag_name.to_s[0] == "+"
-
157
[self, tag_name].to_name
-
end
-
end
-
-
# @return [True/False]
-
1
def field_of? context
-
1054
return false unless junction?
-
1030
if context.present?
-
987
absolute_name(context).left_name.key == context.to_name.key
-
else
-
43
s.match(/^\s*\+[^+]+$/).present?
-
end
-
end
-
-
1
def field_only?
-
21
relative? && stripped.to_name.parts.reject(&:blank?).first == parts.last
-
end
-
-
1
def relative_field_name tag_name
-
field_name(tag_name).name_from self
-
end
-
-
# @return [String]
-
1
def trait tag_code
-
3336
name = trait_name tag_code
-
3336
name.s
-
end
-
-
# @return [Card::Name]
-
1
def trait_name tag_code
-
3561
Card::Name[self, tag_code.to_sym]
-
end
-
-
# @return [True/False]
-
1
def trait_name? *traitlist
-
1400
return false unless junction?
-
1316
right_key = right_name.key
-
1316
traitlist.any? do |codename|
-
1316
Card::Codename.name(codename)&.key == right_key
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Name
-
1
module NameVariants
-
1
@@variant_methods = %i[capitalize singularize pluralize titleize
-
downcase upcase swapcase reverse succ]
-
1
@@variant_aliases = { capitalized: :capitalize, singular: :singularize,
-
plural: :pluralize, title: :titleize }
-
-
1
def vary variants
-
variants.to_s.split(/[\s,]+/).inject(s) do |name, variant|
-
variant = @@variant_aliases[variant.to_sym] || variant.to_sym
-
@@variant_methods.include?(variant) ? name.send(variant) : name
-
end
-
end
-
-
# @return [Card::Name] standardized based on card names
-
1
def standard
-
self.class.compose(parts.map { |part| Card.fetch_name(part) || part })
-
end
-
-
1
def card
-
Card.fetch self, new: {}
-
end
-
-
# @return [Integer] id of card with name
-
1
def card_id
-
Card.fetch_id self
-
end
-
-
# @return [Symbol] codename of card with name
-
1
def codename
-
Codename[card_id]
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
# Card::Query is for finding implicit lists (or counts of lists) of cards.
-
#
-
# Search and Set cards use Card::Query to query the database, and it's also
-
# frequently used directly in code.
-
#
-
# Query "statements" (objects, really) are made in CQL (Card Query
-
# Language). Because CQL is used by Sharks, the primary language
-
# documentation is on decko.org. (https://decko.org/CQL_Syntax). Note that the
-
# examples there are in JSON, like Search card content, but statements in
-
# Card::Query are in ruby form.
-
#
-
# In Decko's current form, Card::Query generates and executes SQL statements.
-
# However, the SQL generation is largely (not yet fully) separated from the
-
# CQL statement interpretation.
-
#
-
# The most common way to use Card::Query is as follows:
-
# list_of_cards = Card::Query.run(statement)
-
#
-
# This is equivalent to:
-
# query = Card::Query.new(statement)
-
# list_of_cards = query.run
-
#
-
# Upon initiation, the query is interpreted, and the following key objects
-
# are populated:
-
#
-
# - @join - an Array of Card::Query::Join objects
-
# - @conditions - an Array of conditions
-
# - @mod - a Hash of other query-altering keys
-
# - @subqueries - a list of other queries nested within this one
-
#
-
# Each condition is either a SQL-ready string (boo) or an Array in this form:
-
# [ field_string_or_sym, Card::Value::Query object ]
-
1
module Query
-
1
require "card/query/clause"
-
1
require "card/query/card_query"
-
1
require "card/query/sql_statement"
-
# Card::Query::CardQuery
-
# After conversion, ATTRIBUTES is a Hash where the key is the attribute
-
# and the value is the attribute type:
-
# { id: :basic, name: :basic, key: :basic ...}
-
# This is used for rapid attribute type lookups in the interpretation phase.
-
ATTRIBUTES = {
-
# Each of the "basic" fields corresponds directly to a database field.
-
# their values are translated fairly directly into SQL-safe values.
-
# (These are referred to as "properties" in CQL documentation. Need to
-
# reconcile #EFM)
-
1
basic: %i[id name key type_id content left_id right_id
-
creator_id updater_id codename read_rule_id],
-
# "Relational" values can involve tying multiple queries together
-
relational: %i[type
-
part left right
-
editor_of edited_by last_editor_of last_edited_by
-
creator_of created_by
-
updater_of updated_by
-
link_to linked_to_by
-
include included_by
-
nest nested_by
-
-
refer_to referred_to_by
-
member_of member
-
-
found_by
-
not sort match name_match complete],
-
-
plus_relational: %i[plus left_plus right_plus],
-
conjunction: %i[and or all any],
-
ignore: %i[prepend append vars],
-
deprecated: %i[view params size]
-
}.each_with_object({}) do |pair, h|
-
58
pair[1].each { |v| h[v] = pair[0] }
-
end
-
-
1
CONJUNCTIONS = { any: :or, in: :or, or: :or, all: :and, and: :and }.freeze
-
-
1
MODIFIERS = %i[conj return sort sort_as group dir limit offset]
-
8
.each_with_object({}) { |v, h| h[v] = nil }
-
-
OPERATORS =
-
9
%w[!= = =~ < > in ~ is].each_with_object({}) { |v, h| h[v] = v }.merge(
-
{ eq: "=", gt: ">", lt: "<", match: "~", ne: "!=",
-
"not in": "not in", "is not": "is not", "!": "is not" }.stringify_keys
-
)
-
-
1
DEFAULT_ORDER_DIRS = { update: "desc", relevance: "desc" }.freeze
-
-
1
class << self
-
1
def new statement, comment=nil
-
732
Query::CardQuery.new statement, comment
-
end
-
-
1
def run statement, comment=nil
-
681
new(statement, comment).run
-
end
-
-
1
def class_for type
-
1337
const_get "#{type.capitalize}Query"
-
end
-
-
1
def safe_sql txt
-
750
txt = txt.to_s
-
750
raise "CQL contains disallowed characters: #{txt}" if txt.match?(/[^\w\s*().,]/)
-
-
750
txt
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
# superclass for CardQuery, ReferenceQuery, ActQuery, and ActionQuery
-
#
-
# Each of the Query classes handle interpretation of hash "statements"
-
# into a number of objects known to the SqlStatement class, including
-
# @conditions, @joins, @comment, and the catch-all @mods
-
#
-
# Sql queries involving multiple tables are made possible by the query
-
# hierarchy as tracked by subqueries (children) and superqueries (parents).
-
# For example, if one card links to another, then this can be represented
-
# as a CardQuery with a ReferenceQuery child that in turn has another
-
# CardQuery as its child.
-
#
-
# See AbstractQuery::Tie for more on how tables can be connected.
-
1
class AbstractQuery
-
1
include QueryHelper
-
1
include Tie
-
-
1
attr_reader :statement, :mods, :conditions, :vars,
-
:subqueries, :superquery, :comment, :negate
-
1
attr_accessor :joins, :conditions_on_join
-
-
1
def initialize statement, _comment=nil
-
2069
@subqueries = []
-
2069
@conditions = []
-
2069
@joins = []
-
2069
@mods = {}
-
-
2069
@statement = statement.clone
-
2069
init_instance_vars :context, :superquery, :fasten, :negate
-
2069
@vars = init_query_vars
-
2069
table_alias
-
end
-
-
1
def init_instance_vars *varnames
-
2069
varnames.each do |varname|
-
8276
instance_variable_set "@#{varname}", (@statement.delete(varname) || nil)
-
end
-
end
-
-
1
def init_query_vars
-
2069
if (v = @statement.delete :vars) then v.symbolize_keys
-
2062
elsif @superquery then @superquery.vars
-
717
else {}
-
end
-
end
-
-
1
def interpret hash
-
886
hash.each do |action, card|
-
886
send action, card
-
end
-
end
-
-
1
def full?
-
false
-
end
-
-
1
def sql
-
720
@sql ||= Query::SqlStatement.new(self).build.to_s
-
end
-
-
1
def root
-
8133
@root ||= @superquery ? @superquery.root : self
-
end
-
-
1
def root?
-
5459
root == self
-
end
-
-
1
def subquery opts={}
-
1345
klass = opts.delete(:class) || Query
-
1345
subquery = klass.new opts.merge(superquery: self)
-
1345
@subqueries << subquery
-
1345
subquery
-
end
-
-
1
def context
-
37
if !@context.nil?
-
37
@context
-
else
-
@context = superquery ? superquery.context : ""
-
end
-
end
-
-
1
def depth
-
2057
@depth ||= case
-
720
when !superquery then 0
-
when fasten == :direct then superquery.depth
-
else superquery.depth + 1
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
1
class AbstractQuery
-
# shared methods for queries
-
1
module QueryHelper
-
1
def direct_subqueries
-
2057
subqueries_with_fasten :direct
-
end
-
-
1
def subqueries_with_fasten fasten
-
2065
list = []
-
2065
subqueries.each do |s|
-
1345
next unless Array.wrap(fasten).include? s.fasten
-
-
8
list << s
-
8
list += s.subqueries_with_fasten s.fasten
-
end
-
2065
list
-
end
-
-
1
def table_alias
-
9355
@table_alias ||= begin
-
2069
if fasten == :direct
-
8
@superquery.table_alias
-
else
-
2061
"#{table_prefix}#{next_table_suffix}"
-
end
-
end
-
end
-
-
1
def next_table_suffix
-
3398
return root.next_table_suffix unless root?
-
-
2061
@table_suffix = (@table_suffix || -1) + 1
-
end
-
-
1
def fld field_name
-
465
"#{table_alias}.#{field_name}"
-
end
-
-
1
def add_condition *args
-
1302
@conditions <<
-
1302
if args.size > 1
-
829
[args.shift, Query::Value.new(args.shift, self)]
-
else
-
473
args[0]
-
end
-
end
-
-
1
def current_conjunction
-
886
"AND"
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
1
class AbstractQuery
-
# The "Tie" methods support tying two queries (CardQuery, ReferenceQuery, etc)
-
# together. The "fasten" variable determines which tying strategy is used.
-
#
-
# We currently support three values for "fasten": :join, :exist, and :in
-
#
-
# In concept, here's how the different strategies would tie table A to table B
-
# in SQL assuming A.id = B.a_id
-
#
-
# - :join ... FROM A JOIN B ON A.id = B.a_id
-
# - :exist ... FROM A WHERE EXISTS (SELECT * FROM B WHERE A.id = B.a_id ...)
-
# - :in ... FROM A WHERE A.id IN (SELECT B.a_id FROM B WHERE ...)
-
#
-
# The different strategies will return the same values but the relative speed is
-
# context dependent.
-
1
module Tie
-
1
def tie subquery_type, val, fields={}, subquery_args={}
-
1337
subquery = tie_subquery subquery_type, subquery_args
-
1337
subquery.interpret val
-
1337
fields = { from: :id, to: :id }.merge fields
-
1337
fasten_tie subquery, fields
-
end
-
-
1
def tie_subquery subquery_type, subquery_args
-
1337
subquery_args[:class] = Query.class_for subquery_type
-
1337
subquery(subquery_args)
-
end
-
-
1
def fasten_tie subquery, fields={}
-
1337
method = "tie_with_#{subquery.fasten}"
-
1337
send method, subquery, fields
-
1337
subquery
-
end
-
-
1
def tie_with_join subquery, fields={}
-
1337
join = Join.new tie_with_join_args(subquery, fields)
-
1337
negate_join(subquery, join, fields) if subquery.negate
-
1337
joins << join
-
end
-
-
1
def tie_with_in subquery, fields
-
subquery.mods[:return] = fields[:to]
-
subquery.mods[:in_field] = fld(fields[:from])
-
end
-
-
1
def tie_with_exist subquery, fields
-
subquery.super_conditions fields if fields.present?
-
end
-
-
1
def fasten
-
7424
@fasten ||= root? ? :join : inherit_fasten
-
end
-
-
1
def tie_with_join_args subquery, fields
-
1337
args = { from: self, from_field: fields[:from],
-
to: subquery, to_field: fields[:to] }
-
1337
args[:side] = :left if left_join? subquery
-
1337
args
-
end
-
-
1
def left_join? subquery
-
1337
current_conjunction == "or" || subquery.negate
-
# reverse conjunction if negated?
-
end
-
-
1
def negate_join subquery, join, fields
-
17
subquery.conditions_on_join = join
-
17
add_condition "#{subquery.fld fields[:to]} is null"
-
end
-
-
1
def inherit_fasten
-
1337
superfasten = superquery.fasten
-
1337
superfasten == :direct ? superquery.inherit_fasten : superfasten
-
end
-
-
1
def super_conditions fields
-
superfield fields[:to], fields[:from]
-
end
-
-
1
def superfield myfield, superfield
-
add_condition "#{fld myfield} = #{superquery.fld superfield}"
-
end
-
-
1
def restrict id_field, val
-
195
if (id = id_from_val(val))
-
164
interpret id_field => id
-
else
-
31
tie :card, val, from: id_field
-
end
-
end
-
-
1
def id_from_val val
-
419
case val
-
125
when Integer then val
-
263
when String then Card.fetch_id(val) || -999
-
when Symbol then Card::Codename.id(val) || -999
-
end
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
# support CQL queries that require the card_acts table
-
1
class ActQuery < AbstractQuery
-
1
def table
-
662
"card_acts"
-
end
-
-
1
def table_prefix
-
331
"cx"
-
end
-
-
1
def action_on card
-
331
tie :action, { action_on: card }, to: :card_act_id
-
end
-
-
1
def update_action_on card
-
tie :action, { update_action_on: card }, to: :card_act_id
-
end
-
-
1
def act_by card
-
tie :card, card, from: :actor_id
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
# support CQL queries that require the card_acts table
-
1
class ActionQuery < AbstractQuery
-
1
def table
-
662
"card_actions"
-
end
-
-
1
def table_prefix
-
331
"cn"
-
end
-
-
1
def action_by card
-
tie :act, { act_by: card }, from: :card_act_id
-
end
-
-
1
def update_action_by card
-
add_update_condition
-
action_by card
-
end
-
-
1
def action_on card
-
331
tie :card, card, from: :card_id
-
end
-
-
1
def update_action_on card
-
add_update_condition
-
action_on card
-
end
-
-
1
def add_update_condition
-
add_condition "#{fld :action_type} = 1"
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
# interpret CQL queries, transform them into SQL, and run them.
-
1
class CardQuery < AbstractQuery
-
1
include Clause
-
1
include Run
-
1
include MatchAttributes
-
1
include RelationalAttributes
-
1
include ReferenceAttributes
-
1
include FoundBy
-
1
include Interpretation
-
1
include Normalization
-
1
include Sorting
-
1
include Conjunctions
-
# Query Execution
-
-
# By default a query returns card objects. This is accomplished by returning
-
# a card identifier from SQL and then hooking into our caching system (see
-
# Card::Fetch)
-
-
1
def self.viewable_sql
-
Card::Query::SqlStatement.new.permission_conditions("cards")
-
end
-
-
1
def table
-
1846
"cards"
-
end
-
-
1
def table_prefix
-
1175
"c"
-
end
-
-
1
def initialize statement, comment=nil
-
1183
super statement
-
1183
@comment = comment || default_comment
-
1183
interpret @statement
-
end
-
-
1
def default_comment
-
816
return if @superquery || !Card.config.sql_comments
-
-
357
statement.to_s
-
end
-
-
# Query Hierarchy
-
# @root, @subqueries, and @superquery are used to track a hierarchy of
-
# query objects. This nesting allows to find, for example, cards that
-
# link to cards that link to cards....
-
-
1
def limit
-
57
mods[:limit].to_i
-
end
-
-
1
def full?
-
1440
!superquery && mods[:return] != "count"
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
1
class CardQuery
-
# conjoining conditions
-
1
module Conjunctions
-
1
def all val
-
conjoin val, :and
-
end
-
1
alias_method :and, :all
-
-
1
def any val
-
1
conjoin val, :or
-
end
-
1
alias_method :or, :any
-
1
alias_method :in, :any
-
-
1
def not val
-
17
tie :card, val, { id: :id }, negate: true
-
end
-
-
1
def current_conjunction
-
1007
@mods[:conj].blank? ? :and : @mods[:conj]
-
end
-
-
1
private
-
-
1
def conjunction val
-
72
return unless [String, Symbol].member? val.class
-
-
CONJUNCTIONS[val.to_sym]
-
end
-
-
1
def conjoin val, conj
-
1
subquery = subquery fasten: :direct, conj: conj
-
1
conjoinable_val(val).each do |val_item|
-
2
subquery.interpret val_item
-
end
-
end
-
-
1
def conjoinable_val val
-
1
return val if val.is_a? Array
-
-
3
clause_to_hash(val).map { |key, value| { key => value } }
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
1
class CardQuery
-
# nest independent query
-
1
module FoundBy
-
1
def found_by val
-
7
found_by_cards(val).compact.each do |card|
-
7
subquery found_by_subquery(card)
-
end
-
end
-
-
1
private
-
-
1
def found_by_subquery card
-
7
found_by_statement(card).merge(fasten: :direct, context: card.name)
-
end
-
-
1
def found_by_statement card
-
7
card&.try(:cql_hash) || invalid_found_by_card!(card)
-
end
-
-
1
def invalid_found_by_card! card
-
raise Card::Error::BadQuery, '"found_by" value must be valid Search, ' \
-
"but #{card.name} is a #{card.type_name}"
-
end
-
-
1
def found_by_cards val
-
7
if val.is_a? Hash
-
Query.run val
-
else
-
7
fetch_found_by_cards val
-
end
-
end
-
-
1
def fetch_found_by_cards val
-
7
Array.wrap(val).map do |v|
-
7
Card.fetch v.to_name.absolute(context), new: {}
-
end
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
1
class CardQuery
-
# Interpret CQL. Once interpreted, SQL can be generated.
-
1
module Interpretation
-
1
INTERPRET_METHOD = { basic: :add_condition,
-
relational: :relate,
-
plus_relational: :relate_compound,
-
conjunction: :send }.freeze
-
-
# normalize and extract meaning from a clause
-
# @param clause [Hash, String, Integer] statement or chunk thereof
-
1
def interpret clause
-
1803
normalize_clause(clause).each do |key, val|
-
2264
interpret_item key, val
-
end
-
end
-
-
1
def interpret_item key, val
-
2264
if interpret_as_content? key
-
3
interpret content: [key, val]
-
2261
elsif interpret_as_modifier? key, val
-
574
interpret_modifier key, val
-
else
-
1687
interpret_attributes key, val
-
end
-
end
-
-
1
def interpret_as_content? key
-
# eg "match" is both operator and attribute;
-
# interpret as attribute when "match" is key
-
2264
OPERATORS.key?(key.to_s) && !ATTRIBUTES[key]
-
end
-
-
1
def interpret_as_modifier? key, val
-
# eg when "sort" is hash, it can have subqueries
-
# and must be interpreted like an attribute
-
2261
MODIFIERS.key?(key) && !val.is_a?(Hash)
-
end
-
-
1
def interpret_modifier key, val
-
574
@mods[key] = val.is_a?(Array) ? val : val.to_s
-
end
-
-
1
def interpret_attributes attribute, val
-
1687
attribute_type = ATTRIBUTES[attribute]
-
1687
if (method = INTERPRET_METHOD[attribute_type])
-
1687
send method, attribute, val
-
else
-
no_method_for_attribute_type attribute, attribute_type
-
end
-
end
-
-
1
def no_method_for_attribute_type attribute, type
-
return if type == :ignore
-
if type == :deprecated
-
deprecated_attribute attribute
-
else
-
bad_attribute! attribute
-
end
-
end
-
-
1
def deprecated_attribute attribute
-
Rails.logger.info "Card queries no longer support #{attribute} attribute"
-
end
-
-
1
def bad_attribute! attribute
-
raise Error::BadQuery, "Invalid attribute: #{attribute}"
-
end
-
-
1
def relate_compound key, val
-
has_multiple_values =
-
72
val.is_a?(Array) &&
-
72
(val.first.is_a?(Array) || conjunction(val.first).present?)
-
72
relate key, val, multiple: has_multiple_values
-
end
-
-
1
def relate key, val, opts={}
-
858
multiple = opts[:multiple].nil? ? val.is_a?(Array) : opts[:multiple]
-
858
method = opts[:method] || :send
-
858
if multiple
-
relate_multi_value method, key, val
-
else
-
858
send method, key, val
-
end
-
end
-
-
1
def relate_multi_value method, key, val
-
conj = conjunction(val.first) ? conjunction(val.shift) : :and
-
if conj == current_conjunction
-
# same conjunction as container, no need for subcondition
-
val.each { |v| send method, key, v }
-
else
-
send conj, (val.map { |v| { key => v } })
-
end
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
1
class CardQuery
-
# Implements the match attributes that match always against content and/or name.
-
# Currently that's different from the match operator that can be restricted to
-
# names or content.
-
# Example: { match: "name or content" } vs. { name: ["match", "a name"] }
-
# TODO: unify handling for both using full text indexing
-
1
module MatchAttributes
-
# match term anywhere in name or content
-
1
def match val
-
8
return unless val.present?
-
5
subconds = %i[name content].map do |field|
-
10
Value.new([:match, val], self).to_sql field
-
end
-
5
add_condition or_join(subconds)
-
end
-
-
# match names beginning with term
-
1
def complete val
-
3
val = val.to_name
-
3
if val.junction?
-
interpret left: val.left
-
interpret right: { complete: val.right } if val.right.present?
-
else
-
3
add_condition "#{table_alias}.key LIKE '#{val.to_name.key}%'"
-
end
-
end
-
-
# match term anywhere in name
-
# DEPRECATE - move handling to name: ["match", val]
-
1
def name_match val
-
interpret name: [:match, val]
-
end
-
-
1
private
-
-
1
def or_join conditions
-
5
"(#{Array(conditions).join ' OR '})"
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
1
class CardQuery
-
# normalize clause's keys and values.
-
1
module Normalization
-
1
def normalize_clause clause
-
1803
clause = clause_to_hash clause
-
1803
clause.symbolize_keys!
-
1803
clause.each do |key, val|
-
2264
next if key.to_sym == :return
-
# when return values are relative, they are relative to the name of the
-
# card returned, not the context card
-
1773
clause[key] = normalize_value val
-
end
-
1803
clause
-
end
-
-
1
def clause_to_hash clause
-
1876
case clause
-
1545
when Hash then clause
-
331
when Integer then { id: clause }
-
when String then { id: (Card::Lexicon.id(clause) || -2) }
-
when Symbol then { id: Card::Codename.id(clause) }
-
else raise Error::BadQuery, "Invalid clause: #{clause.inspect}"
-
end
-
end
-
-
1
def normalize_value val
-
2069
case val
-
1498
when Integer, Float, Hash, Symbol, NilClass then val
-
473
when String then normalize_string_value val
-
98
when Array then normalize_array_value val
-
else raise Error::BadQuery, "Invalid value type: #{val.class} (#{val.inspect})"
-
end
-
end
-
-
1
def normalize_array_value val
-
394
val.map { |v| normalize_value v }
-
end
-
-
1
def normalize_string_value val
-
473
case val.to_s
-
when /^\$(\w+)$/
-
# replace from @vars when value starts with dollar sign
-
8
string_value_from_var Regexp.last_match[1]
-
when /\b_/
-
# absolutize based on @context when there are words beginning with "_"
-
30
val.to_name.absolute(context)
-
else
-
435
val
-
end
-
end
-
-
1
def string_value_from_var varname
-
8
@vars[varname.to_sym].to_s.strip
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
1
class CardQuery
-
# interpret CQL attributes that involve references from one card to another
-
1
module ReferenceAttributes
-
1
def self.define_reference_method methodname, reftype, ref_method, ref_field
-
8
define_method methodname do |val|
-
224
tie :reference,
-
{ ref_method => { reftype: reftype, card: val } },
-
to: ref_field
-
end
-
end
-
-
{
-
1
refer_to: %w[L I],
-
link_to: "L",
-
include: "I",
-
nest: "I"
-
}.each do |methodname, reftype|
-
4
define_reference_method methodname, reftype, :referee, :referer_id
-
end
-
-
{
-
1
referred_to_by: %w[L I],
-
linked_to_by: "L",
-
included_by: "I",
-
nested_by: "I"
-
}.each do |methodname, reftype|
-
4
define_reference_method methodname, reftype, :referer, :referee_id
-
end
-
-
# shortcut methods for role references
-
# DEPRECATE?
-
-
1
def member_of val
-
interpret right_plus: [Card::RolesID, refer_to: val]
-
end
-
-
1
def member val
-
interpret referred_to_by: { left: val, right: Card::RolesID }
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
1
class CardQuery
-
# interpret CQL attributes that relate multiple cards
-
# each method below corresponds to a relational CQL term
-
#
-
# NOTE: methods using "restrict" can be executed without
-
# tying in an additional query if the val in question can be
-
# reduced to an id.
-
1
module RelationalAttributes
-
1
def type val
-
64
restrict :type_id, val
-
end
-
-
1
def part val
-
1
right_val = val.is_a?(Integer) ? val : val.clone
-
1
any(left: val, right: right_val)
-
end
-
-
1
def left val
-
31
restrict :left_id, val
-
end
-
-
1
def right val
-
100
restrict :right_id, val
-
end
-
-
1
def editor_of val
-
331
tie_act :action_on, val
-
end
-
-
1
def updater_of val
-
tie_act :update_action_on, val
-
end
-
-
1
def edited_by val
-
tie_action :action_by, val
-
end
-
-
1
def updated_by val
-
tie_action :update_action_by, val
-
end
-
-
1
def last_editor_of val
-
tie :card, val, to: :updater_id
-
end
-
-
1
def last_edited_by val
-
restrict :updater_id, val
-
end
-
-
1
def creator_of val
-
tie :card, val, to: :creator_id
-
end
-
-
1
def created_by val
-
restrict :creator_id, val
-
end
-
-
# ~~~~~~ PLUS RELATIONAL
-
-
1
def left_plus val
-
junction val, :left, :right_id
-
end
-
-
1
def right_plus val
-
72
junction val, :right, :left_id
-
end
-
-
1
def plus val
-
any(left_plus: val, right_plus: val.deep_clone)
-
end
-
-
1
private
-
-
1
def tie_action action, val
-
tie :action, { action => val }, to: :card_id
-
end
-
-
1
def tie_act action, val
-
331
tie :act, { action => val }, to: :actor_id
-
end
-
-
1
def junction val, side, field
-
72
tie :card, junction_val(val, side), to: field
-
end
-
-
1
def junction_val val, side
-
72
part_clause, junction_clause = val.is_a?(Array) ? val : [val, {}]
-
72
clause_to_hash(junction_clause).merge side => part_clause
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
1
class CardQuery
-
# run CQL queries
-
1
module Run
-
# run the current query
-
# @return [Array] of card objects by default
-
1
def run
-
720
retrn = statement[:return].present? ? statement[:return].to_s : "card"
-
720
return_method = :"return_#{simple_result?(retrn) ? :simple : :list}"
-
720
send return_method, run_sql, retrn
-
end
-
-
# @return [(not an Array)]
-
1
def return_simple sql_result, retrn
-
1135
send result_method(retrn), sql_result, retrn
-
end
-
-
# @return [Array]
-
1
def return_list sql_results, retrn
-
690
large_list sql_results.length if sql_results.length > 1000
-
690
sql_results.map do |record|
-
1105
return_simple record, retrn
-
end
-
end
-
-
1
def large_list length
-
Rails.logger.info "#{length} records returned by #{@statement}"
-
end
-
-
1
def result_method retrn
-
case
-
2270
when respond_to?(:"#{retrn}_result") then :"#{retrn}_result"
-
when (retrn =~ /id$/) then :id_result
-
when (retrn =~ /_\w+/) then :name_result
-
when (retrn == "key") then :key_result
-
else :default_result
-
end
-
end
-
-
1
def count_result results, _field
-
30
results.first["count"].to_i
-
end
-
-
1
def default_result record, field
-
record[field]
-
end
-
-
1
def id_result record, field
-
349
record[field].to_i
-
end
-
-
1
def raw_result record, _field
-
record
-
end
-
-
1
def key_result record, pattern=""
-
22
name_result(record, pattern).to_name.key
-
end
-
-
1
def name_result record, pattern=""
-
633
name = record["name"]&.to_name
-
633
name ||= Card::Lexicon.lex_to_name [record["left_id"], record["right_id"]]
-
633
process_name name, pattern
-
end
-
-
1
def card_result record, _field
-
123
if alter_results?
-
Card.fetch name_result(record), new: {}
-
else
-
123
fetch_or_instantiate record
-
end
-
end
-
-
1
def fetch_or_instantiate record
-
123
card = Card.retrieve_from_cache_by_id record["id"]
-
123
unless card
-
44
card = Card.instantiate record
-
44
Card.write_to_cache card
-
end
-
123
card.include_set_modules
-
123
card
-
end
-
-
1
def run_sql
-
# puts "\nSQL = #{sql}"
-
720
ActiveRecord::Base.connection.select_all sql
-
end
-
-
1
def process_name name, pattern=""
-
633
name = pattern.to_name.absolute(name) if pattern =~ /_\w+/
-
633
return name unless alter_results?
-
-
alter_result name
-
end
-
-
1
def alter_result name
-
name_parts = [statement[:prepend], name, statement[:append]].compact
-
Card::Name[name_parts]
-
end
-
-
1
def simple_result? retrn
-
720
retrn == "count"
-
end
-
-
1
def alter_results?
-
756
statement[:prepend] || statement[:append]
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
1
class CardQuery
-
# handle relational (not simple) CQL sort values
-
#
-
# sorting with subqueries is not fully supported; only a few experimental
-
# examples have been attempted, and even for those the syntax is likely
-
# to change.
-
#
-
# Generally speaking, sorting subqueries will require a JOIN strategy (as
-
# opposed to the "WHERE EXISTS" strategy that is central to queries' main
-
# conditions.)
-
1
module Sorting
-
1
SORT_BY_ITEM_JOIN_MAP = { left: "left_id", right: "right_id" }.freeze
-
-
1
def sort val
-
return nil unless full?
-
-
sort_field = val[:return] || "db_content"
-
val = val.clone
-
item = val.delete(:item) || "left"
-
sort_by val, item, sort_field
-
end
-
-
1
def sort_by val, item, sort_field
-
if sort_field == "count"
-
sort_by_count val, item
-
else
-
sort_by_item_join val, item, sort_field
-
end
-
end
-
-
1
def sort_by_item_join val, item, sort_field
-
join_field = sort_by_item_join_field item
-
join = join_cards val, to_field: join_field,
-
side: "LEFT",
-
conditions_on_join: true
-
@mods[:sort] ||= "#{join.table_alias}.#{sort_field}"
-
end
-
-
1
def sort_by_item_join_field item
-
SORT_BY_ITEM_JOIN_MAP[item.to_sym] || sort_method_not_implemented(:join, item)
-
end
-
-
# EXPERIMENTAL!
-
# sort: { referred_to_by { right: "*follow" }, return count }
-
-
1
def sort_by_count val, item
-
method_name = "sort_by_count_#{item}"
-
sort_method_not_implemented :count, item unless respond_to? method_name
-
send method_name, val
-
end
-
-
1
def sort_method_not_implemented method, item
-
raise Card::Error::BadQuery, "sorting by ##{method}/#{item} not yet implemented"
-
end
-
-
1
def sort_by_count_referred_to val
-
@mods[:sort] = "coalesce(count,0)" # needed for postgres
-
sort_query = count_sort_query
-
sort_query.add_condition "referer_id in (#{count_subselect(val).sql})"
-
# FIXME: - SQL generated before SQL phase
-
-
sort_query.joins << Join.new(from: sort_query, side: "LEFT",
-
to: %w[card_references wr referee_id])
-
join_count_sort_query sort_query
-
end
-
-
1
def join_count_sort_query sort_query
-
sort_query.mods[:sort_join_field] =
-
"#{sort_query.table_alias}.id as sort_join_field"
-
# FIXME: HACK!
-
-
joins << Join.new(from: self, side: "LEFT",
-
to: [sort_query, "srtbl", "sort_join_field"])
-
end
-
-
1
def count_subselect val
-
Query.new val.merge(return: "id", superquery: self)
-
end
-
-
1
def count_sort_query
-
Query.new return: "coalesce(count(*), 0) as count",
-
group: "sort_join_field",
-
superquery: self
-
end
-
-
1
def join_cards val, opts={}
-
conditions_on_join = opts.delete :conditions_on_join
-
s = subquery
-
join_opts = { from: self, to: s }.merge opts
-
card_join = Join.new join_opts
-
joins << card_join unless opts[:from].is_a? Join
-
s.conditions_on_join = card_join if conditions_on_join
-
s.interpret val
-
s
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
1
module Clause
-
# attr_accessor :clause
-
-
1
def safe_sql text
-
750
Query.safe_sql text
-
end
-
-
1
def quote v
-
935
connection.quote(v)
-
end
-
-
1
def connection
-
945
@connection ||= ActiveRecord::Base.connection
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
# object representation of Card::Query joins
-
1
class Join
-
1
JOIN_OPT_KEYS = %i[side conditions
-
from from_table from_alias from_field
-
to to_table to_alias to_field].freeze
-
1
attr_accessor(*JOIN_OPT_KEYS)
-
-
# These two manage hierarchy of nested joins
-
1
attr_accessor :superjoin, :subjoins
-
-
# This example join clause:
-
#
-
# cards c LEFT JOIN card_actions ca on c.id = ca.card_id and ca.draft is null
-
#
-
# ...would translate into the following instance variables on the Join object:
-
#
-
# @side = "left"
-
# @from_table = "cards"
-
# @from_alias = "c"
-
# @from_field = "id"
-
# @to_table = "card_actions"
-
# @to_alias = "ca"
-
# @to_field = "card_id"
-
# @conditions = "ca.draft is null"
-
#
-
# all of the above can be set directly via opts using the keys with the same name.
-
#
-
# Join.new side: "left", from_table: "cards"...
-
#
-
# The from and to fields can also be set via :from and :to keys.
-
# (see #interpret_from_and_to)
-
#
-
# You can generally use Symbols in place of Strings where applicable.
-
#
-
1
def initialize opts={}
-
1337
interpret_from_and_to opts
-
1337
convert_opts_to_instance_variables opts
-
-
1337
@conditions = Array(@conditions).compact
-
1337
@subjoins = []
-
1337
register_superjoin
-
end
-
-
1
def side
-
2674
if !@side.nil?
-
34
@side.to_s.upcase
-
else
-
2640
@side = inside_or? ? "LEFT" : nil
-
end
-
end
-
-
1
def left?
-
1337
side == "LEFT"
-
end
-
-
1
private
-
-
1
def inside_or?
-
2640
from&.is_a?(Card::Query) && from.mods[:conj] == "or"
-
end
-
-
# the options :to and :from can be translated into the full table/alias/field trio.
-
#
-
# - An Array is interpreted in that order (table, alias, field)
-
# - A Hash expects the keys :table, :alias, and (optionally) :field
-
# - A table and alias can be inferred from Card::Query or Card::Query::Reference
-
# objects.
-
# - They can also be inferred from a Join object, but only as a :from value
-
#
-
# In all cases, if the field is not specified, it is assumed to be :id
-
1
def interpret_from_and_to opts
-
1337
%i[from to].each do |side|
-
2674
directional_hash_for_object(side, opts[side]).map do |key, value|
-
8022
opts[:"#{side}_#{key}"] ||= value
-
end
-
end
-
end
-
-
1
def directional_hash_for_object side, object
-
2674
case object
-
when nil then nil
-
when Hash then object
-
when Array then dir_hash(*object)
-
2674
when AbstractQuery then dir_hash_for_query object
-
when Join then dir_hash_for_join side, object
-
else dir_error(side, object)
-
end
-
end
-
-
1
def dir_hash table, table_alias, field=nil
-
2674
hash = { table: table, alias: table_alias }
-
2674
hash[:field] = field || :id
-
2674
hash
-
end
-
-
1
def dir_hash_for_query query
-
2674
dir_hash query.table, query.table_alias
-
end
-
-
1
def dir_hash_for_join side, object
-
raise "to: cannot be Join" if side == :to
-
-
dir_hash object.to_table, object.to_alias
-
end
-
-
1
def dir_error side, object
-
raise Card::Error::BadQuery, "invalid #{side} option: #{object}"
-
end
-
-
1
def convert_opts_to_instance_variables opts
-
1337
opts.each do |key, value|
-
10713
send "#{key}=", value if value.present? && JOIN_OPT_KEYS.member?(key)
-
end
-
end
-
-
1
def register_superjoin
-
1337
return unless @from.is_a? Join
-
-
@superjoin = @from
-
@superjoin.subjoins << self
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
# support the use of the card_references table in CQL
-
1
class ReferenceQuery < AbstractQuery
-
1
def table
-
224
"card_references"
-
end
-
-
1
def table_prefix
-
224
"cr"
-
end
-
-
1
def referer hash
-
88
add_conditions :referer_id, hash
-
end
-
-
1
def referee hash
-
136
add_conditions :referee_id, hash
-
end
-
-
1
def add_conditions outfield, hash
-
224
add_reftype_condition hash[:reftype]
-
224
add_outfield_condition outfield, hash[:card]
-
end
-
-
1
def add_outfield_condition outfield, outcard
-
224
if outcard == "_none"
-
non_outfield
-
224
elsif (id = id_from_val(outcard))
-
224
outfield_id outfield, id
-
else
-
tie :card, outcard, from: outfield
-
end
-
end
-
-
1
def non_outfield
-
add_condition "#{fld :is_present} = 0"
-
end
-
-
1
def outfield_id outfield, id
-
224
add_condition "#{fld(outfield)} = #{id}"
-
end
-
-
1
def add_reftype_condition reftype
-
224
return unless reftype.present?
-
-
224
reftype = Array.wrap reftype
-
224
operator = (reftype.size == 1 ? "=" : "IN")
-
448
quoted_letters = reftype.map { |letter| "'#{letter}'" } * ", "
-
224
add_condition "#{fld(:ref_type)} #{operator} (#{quoted_letters})"
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
# The SqlStatement class generates sql from the Query classes. However, the logic
-
# is not yet as cleanly separated as it should be.
-
-
# At present, SqlStatement contains (imho) too much knowledge about card constructs.
-
# For example, all the permission and trash handling is here.
-
#
-
# In principle, the Query class should "interpret" statements into a few objects and
-
# a clean Query hierarchy. The SqlStatement class should be able to traverse that
-
# hierarchy and do little more than run "to_sql" on its parts, and in so doing
-
# construct a valid SQL statement.
-
1
class SqlStatement
-
1
include Joins
-
1
include Where
-
1
include Order
-
-
1
def initialize query=nil
-
720
@query = query
-
720
@mods = query&.mods
-
end
-
-
1
def build
-
720
@fields = fields
-
720
@tables = tables
-
720
@joins = joins
-
720
@where = where
-
720
@group = group
-
720
@order = order
-
720
@limit_and_offset = limit_and_offset
-
720
self
-
end
-
-
1
def to_s
-
[
-
720
comment, select, from, @joins, @where, @group, @order, @limit_and_offset
-
].compact.join " "
-
end
-
-
1
def select
-
720
"#{leading_space}SELECT DISTINCT #{@fields}"
-
end
-
-
1
def from
-
720
"FROM #{@tables}"
-
end
-
-
1
def leading_space
-
2057
" " * (@query.depth * 2)
-
end
-
-
1
def comment
-
720
return nil unless Card.config.sql_comments && @query.comment
-
-
720
"/* #{@query.comment} */\n"
-
end
-
-
1
def tables
-
720
"#{@query.table} #{@query.table_alias}"
-
end
-
-
1
def fields
-
720
table = @query.table_alias
-
720
field = @mods[:return] unless @mods[:return] =~ /^_\w+/
-
720
field = field.blank? ? :card : field.to_sym
-
720
field = full_field(table, field)
-
720
[field, @mods[:sort_join_field]].compact * ", "
-
end
-
-
1
def full_field table, field
-
720
case field
-
229
when :card, :raw then "#{table}.*"
-
when :content then "#{table}.db_content"
-
119
when :name, :key then "#{table}.name, #{table}.left_id, #{table}.right_id"
-
30
when :count then "coalesce(count( distinct #{table}.id),0) as count"
-
else
-
342
standard_full_field table, field
-
end
-
end
-
-
1
def standard_full_field table, field
-
342
if ATTRIBUTES[field.to_sym] == :basic
-
342
"#{table}.#{field}"
-
else
-
safe_sql field
-
end
-
end
-
-
1
def group
-
720
group = @mods[:group]
-
720
"GROUP BY #{safe_sql group}" if group.present?
-
end
-
-
1
def limit_and_offset
-
720
full_syntax do
-
690
limit = @mods[:limit]
-
690
offset = @mods[:offset]
-
690
if limit.to_i.positive?
-
14
string = "LIMIT #{limit.to_i} "
-
14
string += "OFFSET #{offset.to_i} " if offset.present?
-
14
string
-
end
-
end
-
end
-
-
1
def full_syntax
-
1440
@query.full? ? yield : return
-
end
-
-
1
def safe_sql txt
-
Query.safe_sql txt
-
end
-
-
1
def cast_type type
-
cxn ||= ActiveRecord::Base.connection
-
(val = cxn.cast_types[type.to_sym]) ? val[:name] : safe_sql(type)
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
1
class SqlStatement
-
# transform joins from Card::Query::Join objects to SQL string clause
-
1
module Joins
-
1
def joins query=nil
-
2057
query ||= @query
-
2057
joins_on_query(query).map do |join|
-
1337
join_clause join
-
end.flatten.join " "
-
end
-
-
1
def joins_on_query query
-
2057
query.direct_subqueries.unshift(query).map(&:joins).flatten
-
end
-
-
1
def join_clause join
-
1337
subclause = subjoins join
-
1337
table = join_table join
-
1337
on = on_clause join
-
1337
join_clause_parts(join, table, subclause, on).compact.join " "
-
end
-
-
1
def join_clause_parts join, table, subclause, on
-
1337
parts = ["\n#{leading_space}", join.side, "JOIN"]
-
1337
if join.left? && subclause.present?
-
parts + ["(#{table} #{subclause})", on]
-
else
-
1337
parts + [table, on, subclause]
-
end
-
end
-
-
1
def subjoins join
-
1337
return unless join.to.is_a? AbstractQuery
-
-
1337
joins join.to
-
end
-
-
1
def join_table join
-
1337
to_table = join.to_table
-
1337
to_table = "(#{to_table.sql})" if to_table.is_a? CardQuery
-
1337
[to_table, join.to_alias].join " "
-
end
-
-
1
def on_clause join
-
1337
on_conditions = join.conditions
-
1337
on_conditions.unshift ["#{join.from_alias}.#{join.from_field}",
-
"#{join.to_alias}.#{join.to_field}"].join(" = ")
-
1337
on_conditions += on_card_conditions(join) if join.to.is_a? CardQuery
-
1337
on_conditions.reject!(&:blank?)
-
1337
"ON #{basic_conditions(on_conditions) * ' AND '}"
-
end
-
-
1
def on_card_conditions join
-
451
to = join.to
-
451
explicit = to.conditions_on_join == join ? explicit_conditions(to) : nil
-
451
[explicit, implicit_conditions(to)].compact
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
# convert @query sort rep into order by statement
-
# order information is stored in @mods[:sort], @mods[:sort_as], and
-
# @mods[:dir]
-
1
class SqlStatement
-
ORDER_MAP = {
-
1
"id" => "id",
-
"update" => "updated_at",
-
"create" => "created_at",
-
"name" => "key",
-
"content" => "db_content",
-
"alpha" => "key", # DEPRECATED
-
"relevance" => "updated_at" # DEPRECATED
-
}.freeze
-
-
# build ORDER BY clause
-
1
module Order
-
1
def order
-
720
full_syntax do
-
690
"ORDER BY #{order_directives.join ', '}"
-
end
-
end
-
-
1
def order_directives
-
690
Array.wrap(order_config).map do |order_key|
-
690
order_directive order_key
-
end
-
end
-
-
1
def order_directive order_key
-
690
field = order_field order_key
-
690
@fields += ", #{field}"
-
690
"#{field} #{order_dir order_key}"
-
end
-
-
1
def order_field order_key
-
690
order_as do
-
690
if (field = ORDER_MAP[order_key])
-
690
"#{@query.table_alias}.#{field}"
-
else
-
safe_sql order_key
-
end
-
end
-
end
-
-
1
def order_as
-
690
field = yield
-
690
return field unless (as = @mods[:sort_as])
-
-
"CAST(#{field} AS #{cast_type(safe_sql(as))})"
-
end
-
-
1
def order_dir order_key
-
690
if @mods[:dir].blank?
-
690
DEFAULT_ORDER_DIRS[order_key.to_sym] || "asc"
-
else
-
safe_sql @mods[:dir]
-
end
-
end
-
-
1
def order_config
-
690
@mods[:sort].blank? ? "update" : @mods[:sort]
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
1
class SqlStatement
-
# handle where clause in SqlStatement
-
1
module Where
-
1
def where
-
720
conditions = [explicit_conditions(@query), implicit_conditions(@query)]
-
720
conditions = conditions.reject(&:blank?).join " AND "
-
720
"WHERE #{conditions}" unless conditions.blank?
-
end
-
-
# conditions explicitly specified in the query object
-
1
def explicit_conditions query
-
2065
cond_list = basic_conditions query.conditions
-
2065
cond_list += conditions_from query.subqueries
-
2065
cond_list.reject!(&:blank?)
-
2065
format_conditions cond_list, query
-
end
-
-
# depending on how a query is "fastened", its conditions may be rendered
-
# along with the superquery's
-
1
def conditions_from subqueries
-
2065
subqueries.map do |query|
-
1345
next if query.conditions_on_join
-
-
1328
case query.fasten
-
when :exist then exist_condition query
-
when :in then in_condition query
-
1328
else explicit_conditions query
-
end
-
end
-
end
-
-
1
def exist_condition query
-
"#{maybe_not query}EXISTS (#{spaced_subquery_sql query})"
-
end
-
-
1
def maybe_not query
-
query.negate ? "NOT " : ""
-
end
-
-
1
def in_condition query
-
field = query.mods[:in_field]
-
"#{field} #{maybe_not query}IN (#{spaced_subquery_sql query})"
-
end
-
-
1
def spaced_subquery_sql subquery
-
"\n#{subquery.sql}\n#{leading_space}"
-
end
-
-
# the conditions stored in the query's @conditions variable
-
1
def basic_conditions conditions
-
3402
conditions.map do |condition|
-
3103
case condition
-
2278
when String then condition
-
825
when Array then standard_condition(condition)
-
end
-
end
-
end
-
-
1
def standard_condition condition
-
825
field, val = condition
-
825
val.to_sql field
-
end
-
-
# handle trash and permissions
-
# only applies to card queries
-
1
def implicit_conditions query
-
1171
return unless query.is_a?(CardQuery)
-
-
1171
table = query.table_alias
-
1171
[trash_condition(table), permission_conditions(table)].compact * " AND "
-
end
-
-
1
def trash_condition table
-
1171
"#{table}.trash is false"
-
end
-
-
1
def permission_conditions table
-
1171
return if Auth.always_ok?
-
-
40
read_rules = Auth.as_card.read_rules
-
40
read_rule_list = read_rules.present? ? read_rules.join(",") : 1
-
40
"#{table}.read_rule_id IN (#{read_rule_list})"
-
end
-
-
# convert list of conditions to string
-
1
def format_conditions cond_list, query
-
2065
if cond_list.size > 1
-
556
"(#{cond_list.join condition_joint(query)})"
-
else
-
1509
cond_list.join
-
end
-
end
-
-
1
def condition_joint query
-
556
" #{query.current_conjunction.upcase} "
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
# handling for CQL value clauses, eg [operator, value]
-
1
class Value
-
1
include Clause
-
1
include MatchValue
-
-
1
SQL_FIELD = { name: "key", content: "db_content" }.freeze
-
-
1
attr_reader :query, :operator, :value
-
-
1
def initialize rawvalue, query
-
839
@query = query
-
839
@operator, @value = parse_value rawvalue
-
839
canonicalize_operator
-
end
-
-
1
def to_sql field
-
835
if @operator == "~"
-
10
match_sql field
-
else
-
825
standard_sql field
-
end
-
end
-
-
1
private
-
-
1
def standard_sql field
-
825
@value = Array.wrap(@value).map { |v| v.to_name.key } if field.to_sym == :name
-
825
"#{field_sql field} #{@operator} #{sqlize @value}"
-
end
-
-
1
def parse_value value
-
839
case value
-
36
when Array then parse_array_value value.clone
-
when nil then ["is", nil]
-
803
else ["=", parse_simple_value(value)]
-
end
-
end
-
-
1
def parse_array_value array
-
36
operator = operator?(array.first) ? array.shift : :in
-
172
[operator, array.flatten.map { |i| parse_simple_value i }]
-
end
-
-
1
def parse_simple_value value
-
939
case value
-
939
when String, Integer then value
-
when Symbol then value.to_s
-
when nil then nil
-
else raise Error::BadQuery, "Invalid property value: #{value.inspect}"
-
end
-
end
-
-
1
def canonicalize_operator
-
839
unless (target = OPERATORS[@operator.to_s])
-
raise Error::BadQuery, "Invalid operator: #{@operator}"
-
end
-
-
839
@operator = target
-
end
-
-
1
def operator? key
-
36
OPERATORS.key? key.to_s
-
end
-
-
1
def sqlize v
-
951
case v
-
when Query then v.to_sql
-
26
when Array then sqlize_array v
-
when nil then "NULL"
-
925
else quote(v.to_s)
-
end
-
end
-
-
1
def sqlize_array array
-
26
array.flatten!
-
26
if array.size == 1 && !@operator.in?(["in", "not in"])
-
10
sqlize array.first
-
else
-
132
"(#{array.map { |x| sqlize(x) }.join(',')})"
-
end
-
end
-
-
1
def field_sql field
-
825
"#{@query.table_alias}.#{standardize_field field}"
-
end
-
-
1
def standardize_field field
-
830
SQL_FIELD[field.to_sym] || safe_sql(field.to_s)
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Query
-
1
class Value
-
1
class << self
-
1
def match_prefices
-
1
@match_prefices ||= %w[= ~]
-
end
-
-
1
def match_term_and_prefix_re
-
10
@match_term_and_prefix_re ||=
-
/^(?<prefix>[#{Regexp.escape match_prefices.join}]*)\s*(?<term>.*)$/
-
end
-
end
-
-
# handling for match operator
-
1
module MatchValue
-
1
def match_sql field
-
10
exact_name_match(field) ||
-
"#{match_field field} #{connection.match match_value}"
-
end
-
-
1
def exact_name_match field
-
10
return false unless match_prefix == "=" && field.to_sym == :name
-
"#{field_sql field} = #{quote match_term.to_name.key}"
-
end
-
-
1
def match_field field
-
10
fld = field.to_sym == :name ? "name" : standardize_field(field)
-
10
"#{@query.table_alias}.#{fld}"
-
end
-
-
1
def match_value
-
10
escape_regexp_characters unless match_prefix == "~~"
-
10
quote match_term
-
end
-
-
1
def match_term
-
20
@match_term || (parse_match_term_and_prefix && @match_term)
-
end
-
-
1
def match_prefix
-
20
@match_prefix || (parse_match_term_and_prefix && @match_prefix)
-
end
-
-
# if search val is prefixed with "~~", it is a MYSQL regexp
-
# (and will be passed through)
-
#
-
# Otherwise, all non-alphanumeric characters are escaped.
-
#
-
# A "~" prefix is ignored.
-
-
1
def parse_match_term_and_prefix
-
10
raw_term = Array.wrap(@value).join(" ")
-
10
matches = raw_term.match self.class.match_term_and_prefix_re
-
10
@match_prefix = matches[:prefix] || ""
-
10
@match_term = matches[:term] || ""
-
end
-
-
1
def escape_regexp_characters
-
10
match_term.gsub!(/(\W)/, '\\\\\1')
-
end
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
# a Reference is a directional relationship from one card (the referer)
-
# to another (the referee).
-
1
class Reference < ApplicationRecord
-
1
class << self
-
# bulk insert improves performance considerably
-
# array takes form [ [referer_id, referee_id, referee_key, ref_type], ...]
-
1
def mass_insert array
-
179
return if array.empty?
-
-
926
value_statements = array.map { |values| "\n(#{values.join ', '})" }
-
sql = "INSERT into card_references "\
-
"(referer_id, referee_id, referee_key, ref_type) "\
-
179
"VALUES #{value_statements.join ', '}"
-
179
Card.connection.execute sql
-
end
-
-
# map existing reference to name to card via id
-
1
def map_referees referee_key, referee_id
-
210
where(referee_key: referee_key).update_all referee_id: referee_id
-
end
-
-
# references no longer refer to card, so remove id
-
1
def unmap_referees referee_id
-
82
where(referee_id: referee_id).update_all referee_id: nil
-
end
-
-
# find all references to missing (eg deleted) cards and reset them
-
1
def unmap_if_referee_missing
-
joins(
-
"LEFT JOIN cards ON card_references.referee_id = cards.id"
-
).where(
-
"(cards.id IS NULL OR cards.trash IS TRUE) AND referee_id IS NOT NULL"
-
).update_all referee_id: nil
-
end
-
-
# remove all references from missing (eg deleted) cards
-
1
def delete_if_referer_missing
-
joins(
-
"LEFT JOIN cards ON card_references.referer_id = cards.id"
-
).where(
-
"cards.id IS NULL"
-
).pluck_in_batches(:id) do |group_ids|
-
# used to be .delete_all here, but that was failing on large dbs
-
Rails.logger.info "deleting batch of references"
-
where("id in (#{group_ids.join ','})").delete_all
-
end
-
end
-
-
# repair references one by one (delete, create, delete, create...)
-
# slower, but better than #recreate_all for use on running sites
-
1
def repair_all
-
delete_if_referer_missing
-
Card.where(trash: false).find_each do |card|
-
Rails.logger.info "updating references from #{card}"
-
card.include_set_modules
-
card.update_references_out
-
end
-
end
-
-
# delete all references, then recreate them one by one
-
# faster than #repair_all, but not recommended for use on running sites
-
1
def recreate_all
-
delete_all
-
Card.where(trash: false).find_each do |card|
-
Rails.logger.info "updating references from #{card}"
-
card.include_set_modules
-
card.create_references_out
-
end
-
end
-
end
-
-
# card that refers
-
1
def referer
-
Card[referer_id]
-
end
-
-
# card that is referred to
-
1
def referee
-
Card[referee_id]
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
#
-
# A _Set_ is a group of {Card Cards} to which _Rules_ may apply. Sets can be as
-
# specific as a single card, as general as all cards, or anywhere in between.
-
#
-
# Rules can defined onto Sets in two ways:
-
#
-
# - **Card rules** are defined in card content. These are generally configured via the
-
# web interface and are thus documented at https://decko.org/rules.
-
# - **Code rules** can be defined in a 'set module'.
-
#
-
# The {Card::Mod} docs explain how to create mods and set_modules. This page explains
-
# how those modules become useful.
-
#
-
# Suppose you have created a "mod" for managing your contacts called "contactmanager",
-
# and it includes code that would apply to all +address cards here:
-
#
-
# ./contactmanager/set/right/address.rb
-
#
-
# Then, whenever you fetch or instantiate a +address card, the card will automatically
-
# include code from that set module. In fact, it will include all the set modules
-
# associated with sets of which it is a member.
-
#
-
# For example, say you have a Plaintext card named 'Philipp+address', and you have set
-
# files for the following sets:
-
#
-
# * all cards
-
# * all Plaintext cards
-
# * all cards ending in +address
-
#
-
# When you run this:
-
#
-
# mycard = Card.fetch 'Philipp+address'
-
#
-
# ...then mycard will include the set modules associated with each of those sets in the
-
# above order.
-
#
-
# Note that the set module's filename connects it to the set, so both the set_pattern
-
# and the set_anchor must correspond to the codename of a card in the database to
-
# function correctly.
-
#
-
# A set module is "just ruby", but is generally quite concise because Card uses
-
# a) the set module's file location to autogenerate ruby module names and
-
# b) Card::Set to provide API for the most common set methods.
-
#
-
1
module Set
-
1
require "card/set/event"
-
1
require "card/set/trait"
-
1
require "card/set/basket"
-
1
require "card/set/inheritance"
-
1
require "card/set/format"
-
1
require "card/set/advanced_api"
-
1
require "card/set/helpers"
-
1
require "card/set/i18n_scope"
-
1
require "card/set/loader"
-
-
1
include Event::Api
-
1
include Trait
-
1
include Basket
-
1
include Inheritance
-
-
1
include Format
-
1
include AdvancedApi
-
1
include Helpers
-
-
1
extend I18nScope
-
1
extend Loader
-
-
1
mattr_accessor :modules, :traits
-
-
1
def self.reset_modules
-
1
self.modules = { base: [], base_format: {}, nonbase: {}, nonbase_format: {},
-
abstract: {}, abstract_format: {} }
-
end
-
-
1
reset_modules
-
-
# SET MODULE API
-
#
-
# The most important parts of the set module API are views (see
-
# Card::Set::Format) and events (see Card::Set::Event:Api)
-
end
-
end
-
1
class Card
-
1
module Set
-
1
class Abstract < Pattern::Base
-
end
-
end
-
end
-
1
class Card
-
1
module Set
-
# advanced set module API
-
1
module AdvancedApi
-
1
def ensure_set &block
-
40
set_module = yield
-
40
set_module = card_set_module_const_get(set_module) unless set_module.is_a?(Module)
-
27
set_module
-
rescue NameError => e
-
13
if e.message =~ /uninitialized constant (?:Card::Set::)?(.+)$/
-
13
define_set Regexp.last_match(1)
-
end
-
# try again - there might be another submodule that doesn't exist
-
13
ensure_set(&block)
-
else
-
27
set_module.extend Card::Set
-
end
-
-
1
def attachment name, args
-
2
include_set Abstract::Attachment
-
2
add_attributes name, "remote_#{name}_url".to_sym,
-
:action_id_of_cached_upload, :empty_ok,
-
:storage_type, :bucket, :mod
-
2
uploader_class = args[:uploader] || ::CarrierWave::FileCardUploader
-
2
mount_uploader name, uploader_class
-
2
Card.define_dirty_methods name
-
end
-
-
1
def stage_method method, opts={}, &block
-
class_eval do
-
define_method "_#{method}", &block
-
define_method method do |*args|
-
if (error = wrong_stage(opts) || wrong_action(opts[:on]))
-
raise Card::Error, error
-
end
-
-
send "_#{method}", *args
-
end
-
end
-
end
-
-
1
private
-
-
# @param set_name [String] name of the constant to be defined
-
1
def define_set set_name, start_const=Card::Set
-
13
constant_pieces = set_name.split("::")
-
13
constant_pieces.inject(start_const) do |set_mod, module_name|
-
27
set_mod.const_get_or_set module_name do
-
13
Module.new
-
end
-
end
-
end
-
-
# "set" is the noun not the verb
-
1
def card_set_module_const_get const
-
40
Card::Set.const_get normalize_const(const)
-
end
-
-
1
def normalize_const const
-
40
case const
-
when Array
-
const.map { |piece| piece.to_s.camelcase }.join("::")
-
when Symbol
-
const.to_s.camelcase
-
else
-
40
const
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Set
-
# The purpose of a basket it that you can throw something in from
-
# the same set in another mod.
-
# A basket can be defined on a format or directly on a set
-
#
-
# @example:
-
# # mod/core/set/self/head.rb:
-
# basket :basket_on_set
-
#
-
# format :html do
-
# basket :js_tags # only available in HtmlFormat
-
# view :core { output basket(:js_tags) }
-
# end
-
#
-
# # mod/shell/set/self/head.rb:
-
# add_to_basket :basket_on_set, 'hello world'
-
#
-
# format :html do
-
# add_to_basket :js_tags "<script/>"
-
# add_to_basket :js_tags do |format_obj|
-
# format_obj.render_special_view
-
# end
-
# end
-
1
module Basket
-
# Define a basket in a set or format
-
1
def basket name
-
9
mattr_accessor "#{name}_content"
-
9
send("#{name}_content=", [])
-
9
define_method name do
-
224
send("#{name}_content").map do |item|
-
896
item.respond_to?(:call) ? item.call(self) : item
-
end
-
end
-
end
-
-
# Define a basket in an abstract set
-
1
def abstract_basket name
-
# the basket has to be defined on the including set
-
# (instead on the set itself)
-
-
1
define_singleton_method :included do |host|
-
5
super(host)
-
5
host.basket name
-
end
-
end
-
-
1
def add_to_basket name, content=nil, &block
-
42
content ||= block
-
42
send("#{name}_content").send "<<", content
-
end
-
-
1
def unshift_basket name, content=nil, &block
-
content ||= block
-
send("#{name}_content").unshift content
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Set
-
# Supports the definition of events via the {Api Events API}
-
1
class Event
-
# Events are the building blocks of the three transformative card actions: _create_,
-
# _update_, and _delete_.
-
#
-
# (The fourth kind of action, _read_, does not transform cards, and is associated
-
# with {Card::Format views}, not events).
-
#
-
# As described in detail in {Card::Director}, each act can have many actions, each
-
# action has three phases, each phase has three stages, and each stage has many
-
# events.
-
#
-
# Events are defined in set modules in {Card::Mod **mods**}. Learn more about
-
# {Card::Mod set modules}.
-
#
-
# A simple event definition looks something like this:
-
#
-
# event :append_you_know, :prepare_to_validate, on: :create do
-
# self.content = content + ", you know?"
-
# end
-
#
-
# Note:
-
#
-
# - `:append_you_know` is a unique event name.
-
# - `:prepare_to_validate` is a {Card::Director stage}.
-
# - `on: :create` specifies the action to which the event applies
-
# - `self`, within the event card, is a card object.
-
#
-
# Any card within the {Card::Set set} on which this event is defined will
-
# run this event during the `prepare_to_validate` stage when it is created.
-
#
-
# Events should not be defined within format blocks.
-
1
module Api
-
OPTIONS = {
-
1
on: %i[create update delete save read],
-
changed: Card::Dirty.dirty_options,
-
changing: Card::Dirty.dirty_options,
-
skip: [:allowed],
-
trigger: [:required],
-
when: nil
-
}.freeze
-
-
# Define an event for a set of cards.
-
#
-
# @param event [Symbol] unique event name
-
# @param stage_or_opts [Symbol, Hash] if a Symbol, defines event's
-
# {Card::Director stage}. If a Hash, it's treated as the opts param.
-
# @param opts [Hash] event options
-
# @option opts [Symbol, Array] :on one or more actions in which the event
-
# should be executed. :save is shorthand for [:create, :update]. If no value
-
# is specified, event will fire on create, update, and delete.
-
# @option opts [Symbol, Array] :changed fire event only if field has changed.
-
# valid values: name, content, db_content, type, type_id, left_id, right_id,
-
# codename, trash.
-
# @option opts [Symbol, Array] :changing alias for :changed
-
# @option opts [Symbol] :skip allow actions to skip this event.
-
# (eg. `skip: :allowed`)
-
# @option opts [Symbol] :trigger allow actions to trigger this event
-
# explicitly. If `trigger: :required`, then event will not run unless explicitly
-
# triggered.
-
# @option opts [Symbol, Proc] :when method (Symbol) or code (Proc) to execute
-
# to determine whether to fire event. Proc is given card as argument.
-
# @option opts [Symbol] :before fire this event before the event specified.
-
# @option opts [Symbol] :after fire this event after the event specified.
-
# @option opts [Symbol] :around fire this event before the event specified. This
-
# event will receive a block and will need to call it for the specified
-
# event to fire.
-
# @option opts [Symbol] :stage alternate representation for specifying stage
-
# @option opts [True/False] :after_subcards run event after running subcard events
-
1
def event event, stage_or_opts={}, opts={}, &final
-
191
Event.new(event, stage_or_opts, opts, self, &final).register
-
end
-
end
-
-
1
CONDITIONS = ::Set.new(Api::OPTIONS.keys).freeze
-
-
1
include DelayedEvent
-
1
include Options
-
1
include Callbacks
-
-
1
attr_reader :set_module, :opts
-
-
1
def initialize event, stage_or_opts, opts, set_module, &final
-
191
@event = event
-
191
@set_module = set_module
-
191
@opts = event_opts stage_or_opts, opts
-
191
@event_block = final
-
end
-
-
1
def register
-
191
validate_conditions
-
191
Card.define_callbacks @event
-
191
define_event
-
191
set_event_callbacks
-
end
-
-
# @return the name of the event
-
1
def name
-
9248
@event
-
end
-
-
1
def block
-
191
@event_block
-
end
-
-
# the name for the method that only executes the code
-
# defined in the event
-
1
def simple_method_name
-
474
"#{@event}_without_callbacks"
-
end
-
-
# the name for the method that adds the event to
-
# the delayed job queue
-
1
def delaying_method_name
-
6
"#{@event}_with_delay"
-
end
-
-
1
private
-
-
# EVENT DEFINITION
-
-
1
def define_event
-
191
define_simple_method
-
191
define_event_method
-
end
-
-
1
def define_simple_method
-
191
@set_module.class_exec(self) do |event|
-
191
define_method event.simple_method_name, &event.block
-
end
-
end
-
-
1
def define_event_method
-
191
send "define_#{event_type}_event_method"
-
end
-
-
1
def event_type
-
191
with_delay?(@opts) ? :delayed : :standard
-
end
-
-
1
def define_standard_event_method method_name=simple_method_name
-
191
is_integration = @stage.to_s.match?(/integrate/)
-
191
@set_module.class_exec(@event) do |event_name|
-
191
define_method event_name do
-
6065
rescuing_if_integration is_integration do
-
6065
log_event_call event_name
-
6065
run_callbacks event_name do
-
6064
send method_name
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
-
1
def rescuing_if_integration is_integration
-
6817
is_integration ? rescuing_integration { yield } : yield
-
end
-
-
# one failed integration event should not harm others.
-
1
def rescuing_integration
-
752
yield
-
rescue StandardError => e
-
Card::Error.report e, self
-
ensure
-
true
-
752
end
-
-
1
def log_event_call event
-
6065
Rails.logger.debug "#{name}: #{event}"
-
# puts "#{name}: #{event}"
-
# puts "#{Card::Director.to_s}".green
-
end
-
end
-
1
class Card
-
1
module Set
-
1
class Event
-
1
module Callbacks
-
1
def set_event_callbacks
-
191
%i[before after around].each do |kind|
-
573
next unless (object_method = @opts.delete kind)
-
-
185
set_event_callback object_method, kind
-
end
-
end
-
-
1
def set_event_callback object_method, kind
-
185
valid_event_callback kind, object_method do
-
185
Card.class_exec(self) do |event|
-
185
set_callback object_method, kind, event.name,
-
46379
prepend: true, if: proc { |c| c.event_applies?(event) }
-
end
-
end
-
end
-
-
1
def valid_event_callback kind, method
-
185
yield
-
rescue NoMethodError
-
raise "invalid event callback: `#{kind}: #{method}`"
-
end
-
end
-
end
-
end
-
end
-
1
require "application_job"
-
-
1
class Card
-
# attributes that ActiveJob can handle
-
1
def serializable_attributes
-
95
self.class.action_specific_attributes + set_specific.keys
-
end
-
-
1
module Set
-
1
class Event
-
1
module DelayedEvent
-
1
DELAY_STAGES = ::Set.new(%i[integrate_with_delay_stage
-
integrate_with_delay_final_stage]).freeze
-
-
1
def priority
-
95
@priority || 10
-
end
-
-
1
private
-
-
1
def process_delayed_job_opts opts
-
191
@priority = opts.delete :priority
-
end
-
-
1
def with_delay? opts
-
191
DELAY_STAGES.include?(opts[:after]) || DELAY_STAGES.include?(opts[:before])
-
end
-
-
1
def define_delayed_event_method
-
3
define_event_delaying_method
-
3
define_standard_event_method delaying_method_name
-
end
-
-
# creates a method that creates an ActiveJob that calls the event method.
-
# The scheduled job gets the card object as argument and all serializable
-
# attributes of the card.
-
# (when the job is executed ActiveJob fetches the card from the database
-
# so all attributes get lost)
-
# It uses the event as queue name
-
1
def define_event_delaying_method
-
3
@set_module.class_exec(self) do |event|
-
3
define_method(event.delaying_method_name, proc do
-
95
IntegrateWithDelayJob
-
.set(set_delayed_job_args(event))
-
.perform_later(*perform_delayed_job_args(event))
-
end)
-
end
-
end
-
-
1
class IntegrateWithDelayJob < ApplicationJob
-
1
def perform act_id, card, card_attribs, env, auth, method_name
-
93
handle_perform do
-
93
load_card card, card_attribs
-
93
Director.contextualize_delayed_event act_id, card, env, auth do
-
93
card.send method_name
-
end
-
end
-
end
-
-
1
def handle_perform
-
93
yield
-
rescue StandardError => error
-
Card::Error.report error, @card
-
raise error
-
ensure
-
93
Director.expire
-
end
-
-
1
def load_card card, card_attribs
-
93
@card = card
-
93
Card::Cache.renew
-
93
card.deserialize_for_active_job! card_attribs
-
end
-
end
-
end
-
end
-
end
-
-
1
def deserialize_for_active_job! attr
-
93
attr.each do |attname, val|
-
1599
instance_variable_set("@#{attname}", val)
-
end
-
93
include_set_modules
-
end
-
-
1
private
-
-
1
def set_delayed_job_args event
-
95
{ queue: event.name, priority: event.priority }
-
end
-
-
1
def perform_delayed_job_args event
-
95
[Card::Director.act&.id,
-
self,
-
serialize_for_active_job,
-
Card::Env.serialize,
-
Card::Auth.serialize,
-
event.simple_method_name]
-
end
-
-
1
def serialize_for_active_job
-
95
serializable_attributes.each_with_object({}) do |name, hash|
-
1633
hash[name] = instance_variable_get("@#{name}")
-
end
-
end
-
-
1
def serialize_value value
-
# ActiveJob doesn't accept symbols and Time as arguments
-
case value
-
when Symbol
-
{ value: value.to_s, type: "symbol" }
-
when Time
-
{ value: value.to_s, type: "time" }
-
when Hash
-
{ value: serialize_hash_value(value), type: "hash" }
-
when ActionController::Parameters
-
serialize_value value.to_unsafe_h
-
else
-
{ value: value }
-
end
-
end
-
-
1
def serialize_hash_value value
-
value.each_with_object({}) { |(k, v), h| h[k] = serialize_value(v) }
-
end
-
-
1
def deserialize_value val, type
-
case type
-
when "symbol"
-
val.to_sym
-
when "time"
-
DateTime.parse val
-
when "hash"
-
deserialize_hash_value val
-
else
-
val
-
end
-
end
-
-
1
def deserialize_hash_value value
-
value.each_with_object({}) do |(k, v), h|
-
h[k] = deserialize_value v[:value], v[:type]
-
end
-
end
-
end
-
1
class Card
-
1
module Set
-
1
class Event
-
1
module Options
-
1
def validate_conditions
-
191
@opts.each do |key, val|
-
451
next if key.in? %i[stage before after around]
-
-
266
validate_condition_name key
-
266
validate_condition_value key, val
-
end
-
end
-
-
1
def validate_condition_name condition
-
266
return if CONDITIONS.include? condition
-
-
raise ArgumentError,
-
"invalid condition key '#{condition}' in event '#{@event}'\n" \
-
"valid conditions are #{CONDITIONS.to_a.join ', '}"
-
end
-
-
1
def validate_condition_value condition, val
-
266
if condition == :when
-
64
validate_when_value val
-
else
-
202
invalid = Array.wrap(val) - Api::OPTIONS[condition]
-
202
return if invalid.empty?
-
-
raise ArgumentError,
-
"invalid option#{'s' if invalid.size > 1} '#{invalid}' "\
-
"for condition '#{condition}' in event '#{@event}'"
-
end
-
end
-
-
1
def validate_when_value val
-
64
return if val.is_a?(Symbol) || val.is_a?(Proc)
-
-
raise ArgumentError,
-
"invalid value for condition 'when' in event '#{@event}'\n" \
-
"must be a symbol or a proc"
-
end
-
-
1
def event_opts stage_or_opts, opts
-
191
opts = normalize_opts stage_or_opts, opts
-
191
process_stage_opts opts
-
191
process_action_opts opts
-
191
process_delayed_job_opts opts
-
191
opts
-
end
-
-
1
def normalize_opts stage_or_opts, opts
-
191
if stage_or_opts.is_a? Symbol
-
163
opts[:stage] = stage_or_opts
-
else
-
28
opts = stage_or_opts
-
end
-
191
opts
-
end
-
-
1
def process_action_opts opts
-
191
opts[:on] = %i[create update] if opts[:on] == :save
-
end
-
-
1
def process_stage_opts opts
-
191
stage = opts.delete :stage
-
191
after_subcards = opts.delete :after_subcards
-
191
return if opts[:after] || opts[:before] || opts[:around] || !(@stage = stage)
-
# after, before, or around will override stage configuration
-
-
159
opts[:after] = callback_name stage, after_subcards
-
end
-
-
1
def callback_name stage, after_subcards=false
-
159
name = after_subcards ? "#{stage}_final_stage" : "#{stage}_stage"
-
159
name.to_sym
-
end
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
1
module Set
-
# This document explains how to use format blocks within {Card::Mod mods}. To make
-
# use of it, you will need to understand both {Card::Mod mods} and {Card::Set sets}.
-
#
-
# Within a card mod, you can define format blocks like the following:
-
#
-
# format :html do
-
# def beethoven
-
# :rocks
-
# end
-
# end
-
#
-
# The magic that happens here is that the method #beethoven is now applicable (and
-
# available) _only_ to the cards in the set specified by the mod, and only when
-
# the card is rendering a view in the HTML format.
-
#
-
# If you care, you can certainly learn about how all this works. How the set module
-
# creates a module that looks something like `Card::Set::Type::MyType::HtmlFormat`.
-
# How the format object for a given card in the set includes this module dynamically
-
# when it's initialized. And so on...
-
#
-
# But as monkeys, we don't usually think about all that much, because we work in
-
# the set module space, which lets us focus on the card patterns.
-
#
-
# Speaking of which, there are a few key patterns to be aware of:
-
#
-
# 1. Just as in {Card::Set sets}, format methods for narrower sets will override
-
# format methods for more general sets. So if a #beethoven method is defined
-
# for all cards and again for a specific card type, then the type method will
-
# override the all method when both apply.
-
# 2. Similarly, specific formats inherit from more general formats, and all formats
-
# inherit from the base format. If a format is not specified, the format block
-
# will define methods on the base format class.
-
#
-
# format do
-
# def haydn
-
# :sucks
-
# end
-
# end
-
#
-
# 3. It is possible to use super to refer to overridden methods. For example
-
#
-
# format :html do
-
# def haydn
-
# "<em>#{super}</em>"
-
# end
-
# end
-
#
-
# Note: Set precedence has a higher priority than Format precedence.
-
#
-
# 4. {#view} and {#before} can both be called outside of a format block. They will
-
# be defined on the base format.
-
#
-
# 5. Some very powerful API calls (including {AbstractFormat#view view} and
-
# {AbstractFormat#before before}) are defined in {AbstractFormat}. These methods are
-
# always available in format blocks.
-
1
module Format
-
1
require "card/set/format/haml_paths"
-
1
require "card/set/format/abstract_format"
-
-
# define format behavior within a set module
-
1
def format *format_names, &block
-
17
format_names.compact!
-
17
if format_names.empty?
-
16
format_names = [:base]
-
1
elsif format_names.first == :all
-
format_names =
-
Card::Format.registered.reject { |f| Card::Format.aliases[f] }
-
end
-
17
format_names.each do |f|
-
17
define_on_format f, &block
-
end
-
end
-
-
# shortcut for {AbstractFormat#view} for when #view is called outside of a format
-
# block
-
1
def view *args, &block
-
28
format { view(*args, &block) }
-
end
-
-
# shortcut for {AbstractFormat#before} for when #before is called outside of a
-
# format block
-
1
def before view, &block
-
4
format { before view, &block }
-
end
-
-
1
private
-
-
1
def define_on_format format_name=:base, &block
-
# format class name, eg. HtmlFormat
-
374
klass = Card::Format.format_class_name format_name
-
-
# called on current set module, eg Card::Set::Type::Pointer
-
374
mod = const_get_or_set klass do
-
# yielding set format module, eg Card::Set::Type::Pointer::HtmlFormat
-
254
m = Module.new
-
254
register_set_format Card::Format.class_from_name(klass), m
-
254
m.extend Card::Set::Format::AbstractFormat
-
254
m
-
end
-
374
mod.class_eval(&block)
-
end
-
-
1
def register_set_format format_class, mod
-
531
if all_set?
-
109
all_set_format_mod! format_class, mod
-
else
-
422
format_type = abstract_set? ? :abstract_format : :nonbase_format
-
# ready to include dynamically in set members' format singletons
-
422
format_hash = modules[format_type][format_class] ||= {}
-
422
format_hash[shortname] ||= []
-
422
format_hash[shortname] << mod
-
end
-
end
-
-
# make mod ready to include in base (non-set-specific) format classes
-
1
def all_set_format_mod! format_class, mod
-
109
modules[:base_format][format_class] ||= []
-
109
modules[:base_format][format_class] << mod
-
end
-
-
1
class << self
-
# name of method for layout
-
# used by wrapper
-
1
def layout_method_name layout
-
4
"_layout_#{layout.to_name.key}"
-
end
-
-
# name of method for wrapper
-
# used by wrapped views
-
1
def wrapper_method_name wrapper
-
13
"_wrapper_#{wrapper}"
-
end
-
-
# name of method for view
-
# used by #render
-
1
def view_method_name view
-
44217
"_view_#{view}"
-
end
-
-
# name of method for setting for a given view.
-
# used by #view_setting
-
1
def view_setting_method_name view, setting_name
-
64487
"_view_#{view}__#{setting_name}"
-
end
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
1
module Set
-
1
module Format
-
# AbstractFormat manages the basic format API, including API to define a {#view}.
-
# Whenever you create a format block in a set module in a {Card::Mod mod}, you
-
# create a format module that is extended with AbstractFormat.
-
1
module AbstractFormat
-
1
include Set::Basket
-
1
include ViewOpts
-
1
include ViewDefinition
-
1
include HamlViews
-
1
include Wrapper
-
-
# _Views_ are the primary way that both sharks and monkeys interact with cards.
-
# Sharks select views to use in _nests_. Monkeys can define and tweak those
-
# views. These docs will introduce the basics of view definition.
-
#
-
# ## Sample view definitions
-
#
-
# Here is a very simple view that just defines a label for the card(its name):
-
#
-
# view :label do
-
# card.name
-
# end
-
#
-
# View definitions can take the following forms:
-
#
-
# view :viewname[, option_hash][, &block] # standard
-
# view :viewname, alias_to_viewname[, option_hash] # aliasing
-
#
-
#
-
# ## View definition options
-
#
-
# * __:alias_to__ [Symbol] name of view to which this view should be aliased. View
-
# must already be defined in self or specified mod.
-
#
-
# * __:async__ render view asynchronously by first rendering a card placeholder
-
# and then completing a request. Only applies to HtmlFormat
-
#
-
# * __:cache__ directs how to handle caching for this view. Supported values:
-
# * *:standard* - (default)
-
# * *:always* - cache even when rendered within another cached view
-
# * *:never* - don't ever cache this view. Frequently used to prevent caching
-
# problems
-
#
-
# You should certainly {Card::View::Cache learn more about caching} if you want
-
# to develop mods that are safe in a caching environment.
-
#
-
# * __:compact__ [True/False]. Is view acceptable for rendering inside `compact`
-
# view? Default is false.
-
#
-
# * __:denial__ [Symbol]. View to render if permission is denied. Value can be
-
# any viewname. Default is `:denial`. `:blank` is a common alternative.
-
#
-
# * __:perms__ restricts view permissions. Supported values:
-
# * *:create*, *:read* (default), *:update*, *:delete* - only users with the
-
# given permission for the card viewed.
-
# * *:none* - no permission check; anyone can view
-
# * a *Proc* object. Eg `perms: ->(_fmt) { Auth.needs_setup? }`
-
#
-
# * __:template__ [Symbol] view is defined in a template. Currently `:haml` is
-
# the only supported value. See {HamlViews}
-
#
-
# * __:unknown__ [True/False, Symbol]. Configures handling of "unknown" cards.
-
# (See {Set::All::States card states}). Supported values:
-
# * *true* render view even if card is unknown
-
# * *false* default unknown handling (depends on context, create permissions,
-
# etc)
-
# * a *Symbol*: name of view to render
-
#
-
# * __:wrap__ wrap view dynamically. Value is Symbol for wrapper or Hash with
-
# wrappers and wrapper options. See {Wrapper}
-
#
-
1
def view viewname, *args, &block
-
521
def_opts = process_view_opts viewname, args
-
521
define_view_method viewname, def_opts, &block
-
end
-
-
# simple placeholder for views designed to be overridden elsewhere
-
1
def view_for_override viewname
-
# LOCALIZE
-
view viewname do
-
"override '#{viewname}' view"
-
end
-
end
-
-
# define code to be executed before a view is rendered
-
1
def before view, &block
-
16
define_method "_before_#{view}", &block
-
end
-
-
# Defines a setting method that can be used in all formats. Example:
-
#
-
# format do
-
# setting :cols
-
# cols 5, 7
-
#
-
# view :some_view do
-
# cols # => [5, 7]
-
# end
-
# end
-
#
-
# @param name [Symbol] name of setting. should be available method name
-
1
def setting name
-
2
Card::Set::Format::AbstractFormat.send :define_method, name do |*args|
-
5
define_method name do
-
56
args
-
end
-
end
-
end
-
-
# file location where set mod is stored
-
1
def source_location
-
12
set_module.source_location
-
end
-
-
# @return constant for set module (without format)
-
1
def set_module
-
12
Card.const_get name.split("::")[0..-2].join("::")
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Set
-
1
module Format
-
1
module AbstractFormat
-
# Support haml templates in a Rails like way:
-
# If the view option `template: :haml` is set then a haml template is expected
-
# in a corresponding template path and renders it.
-
#
-
# @example
-
# # mod/core/set/type/basic.rb
-
# view :my_view, template: :haml # uses mod/core/view/type/basic/my_view.haml
-
#
-
# view :with_instance_variables, template: :haml do
-
# @actor = "Mark Haml"
-
# end
-
#
-
# # mod/core/view/type/basic/with_instance_variables.haml
-
# Luke is played by
-
# = actor
-
#
-
# > render :with_instance_variables # => "Luke is played by Mark Haml"
-
1
module HamlViews
-
1
include Card::Set::Format::HamlPaths
-
-
1
private
-
-
1
def haml_view_block view, &block
-
12
path = haml_template_path view
-
12
haml_template_proc ::File.read(path), path, &block
-
end
-
-
1
def haml_template_proc template, path, &block
-
12
proc do
-
with_template_path path do
-
locals = haml_block_locals(&block)
-
haml_to_html template, locals, nil, path: path
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Set
-
1
module Format
-
1
module AbstractFormat
-
# handles definition of view methods
-
1
module ViewDefinition
-
1
mattr_accessor :views
-
182
self.views = Hash.new { |h, k| h[k] = {} }
-
-
1
private
-
-
1
def define_view_method view, def_opts, &block
-
521
view_block = view_block view, def_opts, &block
-
521
view_type = def_opts[:async] ? :async : :standard
-
521
send "define_#{view_type}_view_method", view, &view_block
-
end
-
-
1
def define_standard_view_method view, &block
-
521
views[self][view] = block
-
521
define_method Card::Set::Format.view_method_name(view), &block
-
end
-
-
1
def define_async_view_method view, &block
-
# This case makes only sense for HtmlFormat
-
# but I don't see an easy way to override class methods for a specific
-
# format. All formats are extended with this general module. So
-
# a HtmlFormat.view method would be overridden by AbstractFormat.view
-
# We need something like AbstractHtmlFormat for that.
-
-
view_content = "#{view}_async_content"
-
define_standard_view_method view_content, &block
-
define_standard_view_method view do
-
%(<card-view-placeholder data-url="#{path view: view_content}" />)
-
end
-
end
-
-
1
def view_block view, def_opts, &block
-
521
if (template = def_opts[:template])
-
12
template_view_block view, template, &block
-
509
elsif (alias_to = def_opts[:alias_to])
-
16
alias_view_block view, alias_to, def_opts[:mod], &block
-
else
-
493
block
-
end
-
end
-
-
1
def template_view_block view, template, &block
-
12
return haml_view_block(view, &block) if template == :haml
-
-
raise Card::Error::ServerError, "unknown view template: #{template}"
-
end
-
-
1
def alias_view_block view, alias_to, mod=nil
-
16
mod ||= self
-
16
if block_given?
-
raise Card::Error::ServerError, "no blocks allowed in aliased views"
-
end
-
16
views[mod][alias_to] || begin
-
raise "cannot find #{alias_to} view in #{mod}; " \
-
"failed to alias #{view} from #{self}"
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Set
-
1
module Format
-
1
module AbstractFormat
-
# handles processing of view definition options
-
# (not to be confused with view rendering options. For that, see
-
# {Card::View::Options})
-
1
module ViewOpts
-
# The unknown_ok tag is a global tag for a view and should be used only in
-
# contexts when a Format object is not available. If a format is available,
-
# a format-specific value can be retrieved using view settings.
-
1
mattr_accessor :unknown_ok
-
1
self.unknown_ok = {}
-
-
# view setting values can be accessed from Format objects (eg within format
-
# blocks in set modules) using #view_setting(:setting_name, :view_name)
-
1
VIEW_SETTINGS = %i[cache compact denial perms unknown wrap].freeze
-
-
# view def opts are used in defining views but are not available
-
# at any later point
-
1
VIEW_DEF_OPTS = %i[alias_to mod template async].freeze
-
-
1
private
-
-
1
def process_view_opts view, args
-
521
def_opts, opts = normalize_view_opts args
-
521
interpret_view_settings view, opts
-
521
fail_on_invalid_opts! view, opts
-
521
def_opts
-
end
-
-
1
def fail_on_invalid_opts! view, opts
-
521
return unless opts.present?
-
-
raise Card::Error::ServerError,
-
"unknown view opts for #{view} view: #{opts}"
-
end
-
-
1
def normalize_view_opts args
-
521
def_opts = {}
-
521
def_opts[:alias_to] = args.shift if args[0].is_a?(Symbol)
-
521
opts = args.shift || {}
-
521
VIEW_DEF_OPTS.each do |k|
-
2084
def_opts[k] ||= opts.delete k
-
end
-
521
[def_opts, opts]
-
end
-
-
1
def interpret_view_settings view, opts
-
521
return unless opts.present?
-
248
unknown_ok[view] = true if opts[:unknown] == true
-
-
248
VIEW_SETTINGS.each do |setting_name|
-
1488
define_view_setting_method view, setting_name, opts.delete(setting_name)
-
end
-
end
-
-
1
def define_view_setting_method view, setting_name, setting_value
-
1488
return unless setting_value
-
-
397
method_name = Card::Set::Format.view_setting_method_name view, setting_name
-
15117
define_method(method_name) { setting_value }
-
end
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Set
-
1
module Format
-
1
module AbstractFormat
-
# The Wrapper module provides an API to define wrap methods.
-
# It is available in all formats.
-
1
module Wrapper
-
# Defines a wrapper method with the name "wrap_with_<wrapper_name>".
-
# @param wrapper_name [Symbol, String] the name for the wrap method
-
#
-
# Inside the wrap_block the variable "interior" is always available
-
# to refer to the content that is supposed to be wrapped by the wrapper.
-
#
-
# Example:
-
# wrapper :burger do |opts|
-
# ["#{opts[:bun]}-bun", interior, "bun"].join "|"
-
# end
-
#
-
# It can be used like this:
-
# wrap_with_burger bun: "sesame" do
-
# "meat"
-
# end # => "sesame-bun|meat|bun"
-
#
-
# It's also possible to wrap a whole view with a wrapper
-
# view :whopper, wrap: :burger do
-
# "meat"
-
# end
-
#
-
# Options for view wrappers can be provided using a hash or, if not all wrappers
-
# need options, an array of symbols and hashes.
-
# Example
-
# view :big_mac, wrap: { burger: { bun: "sesame" }, paper: { color: :red } }
-
# view :cheese_burger, wrap: [:burger, paper: { color: :yellow }]
-
#
-
# If you want to define a wrapper that wraps only with a single html tag
-
# then use the following syntax:
-
# wrapper :burger, :div, class: "medium"
-
#
-
# wrap_with_burger "meat" # => "<div class='medium'>meat</div>"
-
1
def wrapper wrapper_name, *args, &wrap_block
-
13
method_name = Card::Set::Format.wrapper_method_name(wrapper_name)
-
13
if block_given?
-
10
define_method method_name, &wrap_block
-
else
-
3
define_tag_wrapper method_name, *args
-
end
-
13
define_wrap_with_method wrapper_name, method_name
-
end
-
-
1
def layout layout, opts={}, &block
-
4
Card::Layout.register_built_in_layout layout, opts
-
4
method_name = Card::Set::Format.layout_method_name(layout)
-
4
define_method method_name, &block
-
4
wrapper layout do
-
send method_name
-
end
-
end
-
-
1
attr_accessor :interior
-
-
1
private
-
-
# expects a tag with options that defines the wrap
-
1
def define_tag_wrapper method_name, tag_name, default_opts={}
-
3
class_eval do
-
3
define_method method_name do |opts={}|
-
130
add_class opts, default_opts[:class]
-
130
wrap_with(tag_name, interior, opts.reverse_merge(default_opts))
-
end
-
end
-
end
-
-
# defines the wrap_with_... method that you call to use the wrapper
-
1
def define_wrap_with_method wrapper_name, wrapper_method_name
-
13
class_exec(self) do |_format|
-
13
define_method "wrap_with_#{wrapper_name}" do |*args, &interior|
-
874
@interior, opts = interior ? [interior.call, args.first] : args
-
-
874
if method(wrapper_method_name).arity.zero?
-
224
send wrapper_method_name
-
else
-
650
send wrapper_method_name, (opts || {})
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Set
-
1
module Format
-
# methods for handling paths to HAML templates
-
1
module HamlPaths
-
1
TEMPLATE_DIR = %w[template set].freeze
-
-
1
def haml_to_html haml, locals={}, a_binding=nil, debug_info={}
-
1266
a_binding ||= binding
-
1266
::Haml::Engine.new(haml).render a_binding, locals || {}
-
rescue Haml::SyntaxError => e
-
raise Card::Error,
-
"haml syntax error #{template_location(debug_info)}: #{e.message}"
-
end
-
-
1
def with_template_path path
-
1266
old_path = @template_path
-
1266
@template_path = path
-
1266
yield
-
ensure
-
1266
@template_path = old_path
-
end
-
-
1
def haml_template_path view=nil, source=nil
-
1278
each_template_path(source) do |template_dir, source_dir|
-
1562
path = try_haml_template_path template_dir, view, source_dir
-
1562
return path if path
-
end
-
msg = "can't find haml template"
-
msg += " for #{view}" if view.present?
-
raise Card::Error, msg
-
end
-
-
1
def template_location debug_info
-
return "" unless debug_info[:path]
-
-
Pathname.new(debug_info[:path]).relative_path_from(Pathname.new(Dir.pwd))
-
end
-
-
1
def each_template_path source
-
1278
source = deep_source(source) || source_location
-
1278
basename = ::File.basename source, ".rb"
-
1278
source_dir = ::File.dirname source
-
1278
["./#{basename}", "."].each do |template_dir|
-
1562
yield template_dir, source_dir
-
end
-
end
-
-
1
TMPSET_REGEXP = %r{(?<carddir>/card)/tmp(sets)?/set/mod\d{3}-(?<modname>[^/]+)/}
-
-
1
def deep_source source
-
1278
return source unless Cardio.config.load_strategy == :tmp_files
-
-
1278
source&.gsub TMPSET_REGEXP do
-
1266
match = Regexp.last_match
-
1266
source_mod_dir match[:modname], match[:carddir]
-
end
-
end
-
-
1
def source_mod_dir modname, carddir
-
1266
prefix = "#{carddir}/mod" unless modname.match?(/^card-mod-/)
-
1266
"#{prefix}/#{modname}/set/"
-
end
-
-
1
def try_haml_template_path template_path, view, source_dir, ext="haml"
-
1562
template_path = File.join(template_path, view.to_s) if view.present?
-
1562
template_path += ".#{ext}"
-
1562
TEMPLATE_DIR.each do |template_dir|
-
2806
path = ::File.expand_path(template_path, source_dir)
-
.sub(%r{(/mod/[^/]+)/set/}, "\\1/#{template_dir}/")
-
2806
return path if ::File.exist?(path)
-
end
-
284
false
-
end
-
-
1
def haml_block_locals &block
-
instance_exec(&block) if block_given?
-
instance_variables.each_with_object({}) do |var, h|
-
h[var.to_s.tr("@", "").to_sym] = instance_variable_get var
-
end
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Set
-
1
module Helpers
-
1
SET_PATTERN_TEST_REGEXP = /^(?<pattern>\w+)_set\?$/
-
-
1
def shortname
-
2675
first = 2 # shortname eliminates Card::Set
-
2675
last = first + num_set_parts(pattern_code)
-
2675
set_name_parts[first..last].join "::"
-
end
-
-
1
def underscore
-
4
shortname.tr(":", "_").underscore
-
end
-
-
1
def num_set_parts pattern_code
-
2675
return 1 if pattern_code == :abstract
-
-
1261
Pattern.find(pattern_code).anchor_parts_count
-
end
-
-
1
def set_name_parts
-
3096
@set_name_parts ||= name.split "::"
-
end
-
-
1
def pattern_code
-
4652
@pattern_code ||= set_name_parts[2].underscore.to_sym
-
end
-
-
# handles all_set?, abstract_set?, type_set?, etc.
-
1
def method_missing method_name, *args
-
1977
if (matches = method_name.match SET_PATTERN_TEST_REGEXP)
-
1977
pattern_code == matches[:pattern].to_sym
-
else
-
super
-
end
-
end
-
-
1
def respond_to_missing? method_name, _include_private=false
-
34482
method_name.match? SET_PATTERN_TEST_REGEXP
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Set
-
1
module I18nScope
-
# return scope for I18n
-
1
def scope backtrace
-
43
parts = path_parts backtrace
-
43
index = path_set_index parts
-
43
mod = mod_from_parts parts, index
-
43
set = set_from_parts parts, index
-
43
"mod.#{mod}.set.#{set}"
-
end
-
-
# extract the mod name from the path of a set's tmp file
-
1
def mod_name backtrace
-
35
parts = path_parts backtrace
-
35
mod_from_parts parts, path_set_index(parts)
-
end
-
-
1
private
-
-
1
def set_from_parts parts, index
-
43
start_index = index + (tmp_files? ? 2 : 1)
-
43
parts[start_index..-1].join "."
-
end
-
-
1
def mod_from_parts parts, set_index
-
78
if tmp_files?
-
78
mod_from_tmp_parts parts, set_index
-
else
-
parts[set_index - 1]
-
end
-
end
-
-
1
def mod_from_tmp_parts parts, set_index
-
78
parts[set_index + 1].gsub(/^[^-]*\-/, "")
-
end
-
-
1
def path_parts backtrace
-
78
parts = find_set_path(backtrace).split(File::SEPARATOR)
-
78
parts[-1] = parts.last.split(".").first
-
78
parts
-
end
-
-
# extract mod and set from real path
-
# @example
-
# if the path looks like ~/mydeck/mod/core/set/all/event.rb/
-
# this method returns ["core", "all", "event"]
-
# def set_path_parts backtrace
-
# parts = path_parts backtrace
-
# res = parts[path_mod_index(parts)..-1]
-
# res.delete_at 1
-
# end
-
-
# extract mod and set from tmp path
-
# @example
-
# a tmp path looks like ~/mydeck/tmp/set/mod002-core/all/event.rb/
-
# this method returns ["core", "all", "event"]
-
# def tmp_set_path_parts backtrace
-
# path_parts = find_tmp_set_path(backtrace).split(File::SEPARATOR)
-
# res = path_parts[tmp_path_mod_index(path_parts)..-1]
-
# res[0] = mod_name_from_tmp_dir res.first
-
# res[-1] = res.last.split(".").first
-
# res
-
# end
-
#
-
# def find_tmp_set_path backtrace
-
# path = backtrace.find { |line| line.include? "tmp/set/" }
-
# raise Error, "couldn't find set path in backtrace: #{backtrace}" unless path
-
#
-
# path
-
# end
-
#
-
#
-
1
def tmp_files?
-
121
Card.config.load_strategy == :tmp_files
-
end
-
-
1
def find_set_path backtrace
-
78
re = %r{(?<!card)/set/}
-
331
path = backtrace.find { |line| line =~ re }
-
78
raise Error, "couldn't find set path in backtrace: #{backtrace}" unless path
-
78
path
-
end
-
-
# # index of the mod part in the tmp path
-
# def tmp_path_mod_index parts
-
# unless (set_index = parts.index("set")) &&
-
# parts.size >= set_index + 2
-
# raise Error, "not a valid set path: #{path}"
-
# end
-
#
-
# set_index + 1
-
# end
-
-
1
def mod_name_from_tmp_dir dir
-
match = dir.match(/^mod\d+-(?<mod_name>.+)$/)
-
match[:mod_name]
-
end
-
-
# index of the mod part in the path
-
1
def path_set_index parts
-
78
unless (set_index = parts.index("set")) &&
-
parts.size >= set_index + 2
-
raise Error, "not a valid set path: #{path}"
-
end
-
-
78
set_index
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Set
-
# API to inherit other sets and their formats
-
-
1
module Inheritance
-
-
# include a set module and all its format modules
-
# @param [Module] set
-
# @param [Hash] opts choose the formats you want to include. You can also
-
# pass arbitrary options to the included set. The option is saved
-
# in the including set. To use the option you need a `included` method
-
# in the included set to fetch the option.
-
# @option opts [Symbol, Array<Symbol>] :only include only these formats
-
# @option opts [Symbol, Array<Symbol>] :except don't include these formats
-
# @example
-
# include_set Type::Basic, except: :css
-
# @example pass an option
-
# include_set Type::Name, default_name: "Luke"
-
# default_name # => "Luke"
-
#
-
# def introduce_yourself
-
# puts my_name_is # => "Luke"
-
# end
-
#
-
# # in Type::Name
-
# def self.included host_class
-
# host_class.class_eval do
-
# define_method :my_name_is do |name=nil|
-
# name || host_class.default_name
-
# end
-
# end
-
# end
-
#
-
1
def include_set set, opts={}
-
128
opts.each do |key, value|
-
cattr_accessor key
-
send "#{key}=", value
-
end
-
-
128
set_type = set.abstract_set? ? :abstract : :nonbase
-
128
add_set_modules Card::Set.modules[set_type][set.shortname]
-
128
include_set_formats set, opts
-
end
-
-
# include format modules of a set
-
# @param [Module] set
-
# @param [Hash] opts choose the formats you want to include
-
# @option opts [Symbol, Array<Symbol>] :only include only these formats
-
# @option opts [Symbol, Array<Symbol>] :except don't include these formats
-
# @example
-
# include_set_formats Type::Basic, except: :css
-
1
def include_set_formats set, opts={}
-
128
each_format set do |format, format_mods|
-
257
format_sym = Card::Format.format_sym format
-
257
next unless applicable_format?(format_sym, opts[:except], opts[:only])
-
-
257
format_mods.each do |format_mod|
-
357
define_on_format format_sym do
-
357
include format_mod
-
end
-
end
-
end
-
end
-
-
1
private
-
-
# iterate through each format associated with a set
-
1
def each_format set
-
128
set_type = set.abstract_set? ? :abstract : :nonbase
-
128
format_type = "#{set_type}_format".to_sym
-
128
modules[format_type].each_pair do |format, set_format_mod_hash|
-
1069
next unless (format_mods = set_format_mod_hash[set.shortname])
-
-
257
yield format, format_mods
-
end
-
end
-
-
1
def applicable_format? format, except, only
-
257
format_sym = Card::Format.format_sym format
-
257
return false if except && Array(except).include?(format_sym)
-
257
return false if only && !Array(only).include?(format_sym)
-
-
257
true
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
1
module Set
-
# the set loading process has two main phases:
-
-
# 1. Definition: interpret each set file, creating/defining set and
-
# set_format modules
-
# 2. Organization: have base classes include modules associated with the
-
# 'all' set, and clean up the other modules
-
1
module Loader
-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
# Definition Phase
-
-
# each set file calls `extend Card::Set` when loaded
-
1
def extended mod
-
450
register_set mod
-
end
-
-
# make the set available for use
-
1
def register_set set_module
-
451
if set_module.all_set?
-
# automatically included in Card class
-
136
modules[:base] << set_module
-
else
-
315
set_type = set_module.abstract_set? ? :abstract : :nonbase
-
# made ready for dynamic loading via #include_set_modules
-
315
modules[set_type][set_module.shortname] ||= []
-
315
modules[set_type][set_module.shortname] << set_module
-
end
-
end
-
-
#
-
# When a Card application loads, it uses set modules to autogenerate
-
# tmp files that add module names (Card::Set::PATTERN::ANCHOR) and
-
# extend the module with Card::Set.
-
-
#
-
-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
# Organization Phase
-
-
# 'base modules' are modules that are always included on the Card or
-
# Format class
-
# 'nonbase modules' are included dynamically on singleton_classes
-
1
def process_base_modules
-
1
return unless modules[:base].present?
-
-
1
Card.add_set_modules modules[:base]
-
1
modules[:base_format].each do |format_class, modules_list|
-
11
format_class.add_set_modules modules_list
-
end
-
1
modules[:base].clear
-
1
modules[:base_format].clear
-
end
-
-
1
def clean_empty_modules
-
1
clean_empty_module_from_hash modules[:nonbase]
-
1
modules[:nonbase_format].values.each do |hash|
-
11
clean_empty_module_from_hash hash
-
end
-
end
-
-
1
def clean_empty_module_from_hash hash
-
12
hash.each do |mod_name, modlist|
-
1089
modlist.delete_if { |x| x.instance_methods.empty? }
-
493
hash.delete mod_name if modlist.empty?
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Set
-
1
class Pattern
-
1
class << self
-
1
def reset
-
nonbase_loadables.each do |set_pattern|
-
Card::Set.const_remove_if_defined set_pattern.to_s.split("::").last
-
end
-
Card.set_patterns = []
-
@card_keys = nil
-
end
-
-
1
def loadables
-
Card.set_patterns.push(Card::Set::Abstract).reverse
-
end
-
-
1
def nonbase_loadables
-
l = loadables
-
l.delete Card::Set::All
-
l
-
end
-
-
1
def find pattern_code
-
8280
Card.set_patterns.find { |sub| sub.pattern_code == pattern_code }
-
end
-
-
1
def card_keys
-
@card_keys ||=
-
Card.set_patterns.each_with_object({}) do |set_pattern, hash|
-
card_key = Card.quick_fetch(set_pattern.pattern_code).key
-
hash[card_key] = true
-
end
-
end
-
-
1
def nonbase_loadable_codes
-
l = loadable_codes
-
l.delete :all
-
l
-
end
-
-
1
def loadable_codes
-
29
Card.set_patterns.map(&:pattern_code).push(:abstract).reverse
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Set
-
1
class Pattern
-
# class from which set patterns inherit
-
1
class Base
-
1
class << self
-
1
attr_accessor :pattern_code, :pattern_id, :junction_only,
-
:assigns_type, :anchorless
-
1
attr_writer :anchor_parts_count
-
-
1
def new card
-
69066
super if pattern_applies? card
-
end
-
-
1
def register pattern_code, opts={}
-
9
if (self.pattern_id = Card::Codename.id(pattern_code))
-
9
self.pattern_code = pattern_code
-
9
Card.set_patterns.insert opts.delete(:index).to_i, self
-
9
self.anchorless = !respond_to?(:anchor_name)
-
17
opts.each { |key, val| send "#{key}=", val }
-
else
-
warn "no codename for pattern_code #{pattern_code}"
-
end
-
end
-
-
1
def junction_only?
-
38370
junction_only == true
-
end
-
-
1
def anchorless?
-
147449
anchorless
-
end
-
-
1
def pattern
-
954
Card.fetch(pattern_id, skip_modules: true).name
-
end
-
-
1
def pattern_applies? card
-
38370
junction_only? ? card.name.junction? : true
-
end
-
-
1
def anchor_parts_count
-
60579
@anchor_parts_count ||= (anchorless? ? 0 : 1)
-
end
-
-
1
def module_key anchor_codes
-
52905
return pattern_code.to_s.camelize if anchorless?
-
34147
return unless anchor_codes # is this not an error?
-
-
80932
([pattern_code] + anchor_codes).map { |code| code.to_s.camelize }.join "::"
-
end
-
-
# label for set pattern if no anchor is given
-
1
def generic_label
-
label nil
-
end
-
end
-
-
# Instance methods
-
-
1
def initialize card
-
40353
return if self.class.anchorless?
-
22845
@anchor_name = self.class.anchor_name(card).to_name
-
22845
@anchor_id = find_anchor_id card
-
end
-
-
1
def find_anchor_id card
-
22845
self.class.try(:anchor_id, card) || Card.fetch_id(@anchor_name)
-
end
-
-
1
def module_key
-
186560
return @module_key if defined? @module_key
-
52905
@module_key = self.class.module_key anchor_codenames
-
end
-
-
1
def lookup_module_list modules_hash
-
83445
module_key && modules_hash[module_key]
-
end
-
-
1
def module_list
-
62144
lookup_module_list Card::Set.modules[:nonbase]
-
end
-
-
1
def format_module_list klass
-
52132
hash = Card::Set.modules[:nonbase_format][klass]
-
52132
hash && lookup_module_list(hash)
-
end
-
-
1
def anchor_codenames
-
52905
anchor_parts.map do |part|
-
58653
part_id = Card.fetch_id part
-
58653
Card::Codename[part_id] || break
-
end
-
end
-
-
1
def anchor_parts
-
52905
return [@anchor_name] unless anchor_parts_count > 1
-
-
6042
parts = @anchor_name.parts
-
6042
if parts.size <= anchor_parts_count
-
6042
parts
-
else
-
# handles cases where anchor is a compound card, eg A+B+*self
-
[@anchor_name[0..-anchor_parts_count]] + parts[(-anchor_parts_count + 1)..-1]
-
end
-
end
-
-
1
def anchor_parts_count
-
58947
self.class.anchor_parts_count
-
end
-
-
1
def pattern
-
954
@pattern ||= self.class.pattern
-
end
-
-
1
def to_s
-
954
self.class.anchorless? ? pattern.s : "#{@anchor_name}+#{pattern}"
-
end
-
-
1
def inspect
-
"<#{self.class} #{to_s.to_name.inspect}>"
-
end
-
-
1
def safe_key
-
7223
caps_part = self.class.pattern_code.to_s.tr(" ", "_").upcase
-
7223
self.class.anchorless? ? caps_part : "#{caps_part}-#{@anchor_name.safe_key}"
-
end
-
-
1
def rule_set_key
-
46006
if self.class.anchorless?
-
21792
self.class.pattern_code.to_s
-
24214
elsif @anchor_id
-
18887
"#{@anchor_id}+#{self.class.pattern_code}"
-
end
-
end
-
end
-
-
1
module Helper
-
1
private
-
-
1
def left_type card
-
4380
card.superleft&.type_name || quick_type(card.name.left_name)
-
end
-
-
1
def quick_type name
-
4042
if name.present?
-
4014
card = Card.fetch name, skip_modules: true, new: {}
-
4014
card.include_set_modules if card.new? && name.to_name.junction?
-
4014
card&.type_name
-
else
-
28
"RichText"
-
end
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Set
-
1
class RequiredField
-
1
attr_reader :parent_set, :field, :options
-
-
1
def initialize parent_set, field, options={}
-
2
@parent_set = parent_set
-
2
@field = field
-
2
@options = options
-
end
-
-
1
def add
-
2
create_parent_event
-
2
return unless field_events?
-
-
1
define_field_test
-
1
create_field_events
-
end
-
-
1
def parent_event_name
-
2
[parent_set.underscore, "requires_field", field].join("__").to_sym
-
end
-
-
1
def field_event_name action
-
2
[field, "required_by", parent_set.underscore, "on", action].join("__").to_sym
-
end
-
-
1
private
-
-
1
def define_field_test
-
1
return unless (test = event_test)
-
method_name = field_test_name
-
field_set.class_exec do
-
define_method method_name do
-
left.send test
-
end
-
end
-
end
-
-
1
def field_test_name
-
return unless event_test
-
"_when_left_#{event_test}".to_sym
-
end
-
-
1
def event_test
-
3
return @event_test unless @event_test.nil?
-
1
test = options[:when]
-
1
@event_test = test&.is_a?(Symbol) ? test : false
-
end
-
-
1
def field_set
-
2
@field_set ||= ensure_field_set parent_set, field
-
end
-
-
# for now, we only support field events on type sets. That's because only type sets
-
# have fields that are set-addressable (via type plus right sets)
-
1
def field_events?
-
2
parent_set.type_set?
-
end
-
-
1
def create_field_events
-
1
create_field_event :delete, "deleted", :trashed_left?
-
1
create_field_event :update, "renamed", :same_field?, changing: :name
-
end
-
-
1
def field_event_options action, extra_options
-
2
options = { on: action }.merge extra_options
-
2
options[:when] = field_test_name if event_test
-
2
options
-
end
-
-
1
def create_field_event action, action_verb, allow_test, extra_options={}
-
2
event_name = field_event_name action
-
2
event_options = field_event_options action, extra_options
-
2
field_set.class_exec(self) do |required|
-
2
event event_name, :validate, event_options do
-
return if send allow_test
-
-
errors.add required.field, "can't be #{action_verb}; required field"
-
end
-
end
-
end
-
-
1
def create_parent_event
-
2
parent_set.class_exec(self) do |required|
-
2
event required.parent_event_name, :validate,
-
required.options.merge(on: :create) do
-
3
return if field?(required.field) || left&.type_id == Card::CardtypeID
-
-
# Without the Cardtype exemption, we can get errors on type plus right sets
-
# eg, if right/account has require_field :email, then when we're trying
-
# to create User+*account+*type_plus right rules, it fails, because
-
# User+*account doesn't have an +email field.
-
#
-
# Need a better solution so we can require fields on cardtype+X cards, too.
-
-
errors.add required.field, "required" # LOCALIZE
-
end
-
end
-
end
-
-
1
def ensure_field_set parent_set, field
-
4
field_set = parent_set.ensure_set { field_set_name parent_set, field }
-
1
Card::Set.register_set field_set
-
1
field_set
-
end
-
-
1
def field_set_name parent_set, field
-
3
"TypePlusRight::#{parent_set.set_name_parts.last}::#{field.to_s.capitalize}"
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Set
-
# ActiveCard support: accessing plus cards as attributes
-
1
module Trait
-
1
def card_accessor *args
-
11
options = args.extract_options!
-
11
add_traits args, options.merge(reader: true, writer: true)
-
end
-
-
1
def card_reader *args
-
3
options = args.extract_options!
-
3
add_traits args, options.merge(reader: true)
-
end
-
-
1
def card_writer *args
-
options = args.extract_options!
-
add_traits args, options.merge(writer: true)
-
end
-
-
1
def require_field *fields
-
2
options = fields.last.is_a?(Hash) ? fields.pop : {}
-
2
fields.each do |field|
-
2
Card::Set::RequiredField.new(self, field, options).add
-
end
-
end
-
-
1
private
-
-
1
def add_attributes *args
-
2
Card.set_specific_attributes ||= []
-
2
Card.set_specific_attributes += args.map(&:to_s)
-
2
Card.set_specific_attributes.uniq!
-
end
-
-
1
def get_traits mod
-
14
Card::Set.traits ||= {}
-
14
Card::Set.traits[mod] || Card::Set.traits[mod] = {}
-
end
-
-
1
def add_traits args, options
-
14
mod = self
-
14
mod_traits = get_traits mod
-
-
14
new_opts = options[:type] ? { type: options[:type] } : {}
-
14
new_opts[:default_content] = options[:default] if options[:default]
-
-
14
args.each do |trait|
-
14
define_trait_card trait, new_opts
-
14
define_trait_reader trait if options[:reader]
-
14
define_trait_writer trait if options[:writer]
-
-
14
mod_traits[trait.to_sym] = options
-
end
-
end
-
-
1
def define_trait_card trait, opts
-
14
define_method "#{trait}_card" do
-
1256
fetch trait.to_sym, new: opts.clone, eager_cache: true
-
end
-
end
-
-
1
def define_trait_reader trait
-
14
define_method trait do
-
358
send("#{trait}_card").content
-
end
-
end
-
-
1
def define_trait_writer trait
-
11
define_method "#{trait}=" do |value|
-
card = send "#{trait}_card"
-
subcards.add name: card.name, type_id: card.type_id, content: value
-
instance_variable_set "@#{trait}", value
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Set
-
1
class Type < Pattern::Base
-
1
def initialize card
-
6411
super
-
# support type inheritance
-
6411
@inherit_card = card unless module_key
-
end
-
-
1
def lookup_module_list modules_hash
-
29843
lookup_key = module_key || inherited_key
-
29843
modules_hash[lookup_key] if lookup_key
-
end
-
-
1
private
-
-
1
def inherited_key
-
702
if defined?(@inherited_key)
-
424
@inherited_key
-
else
-
278
@inherited_key = lookup_inherited_key
-
end
-
end
-
-
1
def lookup_inherited_key
-
278
return unless (card = @inherit_card)
-
-
278
@inherit_card = nil
-
278
return unless (type_code = default_type_code card)
-
-
278
mod_key = "Type::#{type_code.to_s.camelize}"
-
278
mod_key if mods_exist_for_key? mod_key
-
end
-
-
1
def default_type_code card
-
278
card.rule_card(:default)&.type_code
-
end
-
-
1
def mods_exist_for_key? mod_key
-
278
list_of_hashes = Card::Set.modules[:nonbase_format].values
-
278
list_of_hashes << Card::Set.modules[:nonbase]
-
556
list_of_hashes.any? { |h| h[mod_key] }
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
# API to create/update/delete additional cards together with the main card.
-
# The most common case is for fields but subcards don't have to be descendants.
-
#
-
# Subcards can be added as card objects or attribute hashes.
-
#
-
# Use the methods defined in core/set/all/subcards.rb
-
# Example
-
# Together with "my address" you want to create the subcards
-
# "my address+name", "my address+street", etc.
-
1
class Subcards
-
1
include Add
-
1
include Remove
-
1
include Relate
-
-
1
attr_accessor :context_card, :keys
-
1
def initialize context_card
-
545
@context_card = context_card
-
545
@keys = ::Set.new
-
end
-
-
1
def [] name
-
card(name) || field(name)
-
end
-
-
1
def field name
-
147
key = field_name_to_key name
-
147
fetch_subcard key if @keys.include? key
-
end
-
-
1
def card name
-
return unless @keys.include? name.to_name.key
-
-
fetch_subcard name
-
end
-
-
1
def present?
-
27
@keys.present?
-
end
-
-
1
def catch_up_to_stage stage_index
-
each_card do |subcard|
-
subcard.catch_up_to_stage stage_index
-
end
-
end
-
-
1
def rename old_name, new_name
-
88
return unless @keys.include? old_name.to_name.key
-
-
@keys.delete old_name.to_name.key
-
@keys << new_name.to_name.key
-
end
-
-
1
def respond_to_missing? method_name, _include_private=false
-
@keys.respond_to? method_name
-
end
-
-
1
def method_missing method, *args
-
return unless respond_to_missing?(method)
-
-
@keys.send method, *args
-
end
-
-
# fetch all cards first to avoid side effects
-
# e.g. deleting a user adds follow rules and +*account to subcards
-
# for deleting but deleting follow rules can remove +*account from the
-
# cache if it belongs to the rule cards
-
1
def cards
-
3421
@keys.map do |key|
-
1330
fetch_subcard key
-
end.compact
-
end
-
-
1
def each_card
-
3421
cards.each do |card|
-
1330
yield card
-
end
-
end
-
-
1
alias_method :each, :each_card
-
-
1
def each_with_key
-
451
@keys.each do |key|
-
201
card = fetch_subcard(key)
-
201
yield(card, key) if card
-
end
-
end
-
-
1
def fetch_subcard key
-
1702
Card.fetch key, local_only: true, new: {}
-
end
-
-
1
private
-
-
1
def subcard_key cardish
-
24
key = case cardish
-
when Card then cardish.key
-
when Symbol then fetch_subcard(cardish).key
-
24
else cardish.to_name.key
-
end
-
24
key = absolutize_subcard_name(key).key unless @keys.include?(key)
-
24
key
-
end
-
-
1
def absolutize_subcard_name name
-
253
name = Card::Name[name]
-
253
return name if @context_card.name.parts.first.blank?
-
-
253
name.absolute_name @context_card.name
-
end
-
end
-
end
-
1
class Card
-
1
class Subcards
-
# Methods for adding subcards
-
1
module Add
-
# @example Add a subcard with name 'spoiler'
-
# add 'spoiler', type: 'Phrase', content: 'John Snow is a Targaryen'
-
# card_obj = Card.new name: 'spoiler', type: 'Phrase',
-
# content: 'John Snow is a Targaryen'
-
# add card_obj
-
# add name: 'spoiler', type: 'Phrase', content: 'John Snow is a Targaryen'
-
#
-
# @example Add a subcard that is added in the integration phase
-
# (and hence doesn't hold up the transaction for the main card)
-
# add 'spoiler', content: 'John Snow is a Targaryen'
-
# add card_obj, delayed: true
-
-
1
def << value
-
add value
-
end
-
-
1
def []= name, card_or_attr
-
case card_or_attr
-
when Hash
-
new_by_attributes name, card_or_attr
-
when Card
-
new_by_card card_or_attr
-
end
-
end
-
-
1
def add *args
-
496
case args.first
-
87
when Card then new_by_card args.first
-
357
when Hash then add_hash args.first
-
52
else new_by_attributes(*args)
-
end
-
end
-
-
1
def add_hash hash
-
357
if (name = hash.delete :name)
-
new_by_attributes name, hash
-
else
-
357
multi_add hash
-
end
-
end
-
-
1
def add_child name, args
-
7
name = name.is_a?(Symbol) ? name.cardname : name.to_name
-
7
add name.prepend_joint, args
-
end
-
1
alias_method :add_field, :add_child
-
-
1
def new_by_card card
-
340
card.supercard = @context_card
-
340
if !card.name.simple? && card.name.field_of?(@context_card.name)
-
211
card.superleft = @context_card
-
end
-
340
@keys << card.key
-
340
Card.write_to_soft_cache card
-
340
card.director = @context_card.director.subdirectors.add card
-
340
card
-
end
-
-
1
def new_by_attributes name, attributes={}
-
253
attributes ||= {}
-
253
absolute_name = absolutize_subcard_name name
-
253
subcard_args = extract_subcard_args! attributes
-
253
card = initialize_by_attributes absolute_name, attributes
-
253
subcard = new_by_card card
-
253
card.subcards.add subcard_args
-
253
subcard
-
end
-
-
1
def initialize_by_attributes name, attributes
-
253
Card.assign_or_newish name, attributes, local_only: true
-
end
-
-
# TODO: this method already exists as card instance method in
-
# tracked_attributes.rb. Find a place for it where its accessible
-
# for both. There is one important difference. The keys are symbols
-
# here instead of strings
-
1
def extract_subcard_args! args
-
253
subcards = args.delete(:subcards) || {}
-
253
if (subfields = args.delete(:subfields))
-
subfields.each_pair do |key, value|
-
subcards[normalize_subfield_key(key)] = value
-
end
-
end
-
253
args.keys.each do |key|
-
270
subcards[key] = args.delete(key) if key =~ /^\+/
-
end
-
253
subcards
-
end
-
-
1
private
-
-
# ensure a leading '+'
-
1
def normalize_subfield_key key
-
key = Card::Codename.name(key) if key.is_a?(Symbol) && Card::Codename.exist?(key)
-
key.to_name.prepend_joint
-
end
-
-
# Handles hash with several subcards
-
1
def multi_add args
-
357
args.each_pair do |key, val|
-
201
case val
-
when String, Array, Integer
-
2
new_by_attributes key, content: val
-
when Card
-
val.name = absolutize_subcard_name key
-
new_by_card val
-
when nil
-
next
-
else
-
199
new_by_attributes key, val
-
end
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Subcards
-
# Methods for handling related subcards
-
1
module Relate
-
1
def field_name_to_key name
-
147
if @context_card.name.starts_with_joint?
-
relative_child(name).key
-
else
-
147
child(name).key
-
end
-
end
-
-
1
def child name
-
147
absolute_name = @context_card.name.field_name name
-
147
if @keys.include? absolute_name.key
-
147
absolute_name
-
else
-
relative_child name
-
end
-
end
-
-
1
def relative_child name
-
@context_card.name.relative_field_name name
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Subcards
-
# Methods for removing/clearing subcards
-
1
module Remove
-
1
def remove_child cardish
-
child = cardish.is_a?(Card) ? cardish : child(cardish)
-
remove child
-
end
-
1
alias_method :remove_field, :remove_child
-
-
1
def remove name_or_card
-
24
key = subcard_key name_or_card
-
24
return unless @keys.include? key
-
-
24
@keys.delete key
-
24
clear_key key
-
end
-
-
1
def clear
-
@keys.each { |key| clear_key key }
-
@keys = ::Set.new
-
end
-
-
1
def clear_key key
-
24
if (subcard = fetch_subcard key)
-
24
Director.deep_delete subcard.director
-
24
subcard.current_action&.delete
-
end
-
24
Card.cache.soft.delete key
-
24
subcard
-
end
-
-
1
def deep_clear cleared=::Set.new
-
each_card do |card|
-
next if cleared.include? card.id
-
-
cleared << card.id
-
card.subcards.deep_clear cleared
-
end
-
clear
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
1
module Version
-
1
class << self
-
1
def release
-
224
@@version ||= File.read(File.expand_path("../../VERSION", __dir__)).strip
-
end
-
end
-
end
-
end
-
1
class Card
-
# Card::View manages {Options view options}, {Cache view caching}, and
-
# {Permission view permissions}.
-
#
-
# View objects, which are instantiated whenever a view is rendered, are available as
-
# in views and other format methods. The view objects can be accessed using `#voo`.
-
# We sometimes feebly pretend VOO is an acronym for "view option object," but really
-
# we just needed a way not to confuse these Card::View options with the countless
-
# references to viewnames that naturally arise when rendering views within views within
-
# views.
-
#
-
# When view A renders view B within the same format object, A's voo is the parent of
-
# B's voo. When card C nests card D, a new (sub)format object is initialized. C is then
-
# the parent _format_ of D, but D has its own root voo.
-
#
-
# So a lineage might look something like this:
-
#
-
# `F1V1 -> F1V2 -> F1V3 -> F2V1 -> F2V2 -> F3V1 ...`
-
#
-
#
-
1
class View
-
1
include Options
-
1
include View::Cache
-
1
include Classy
-
1
include Permission
-
-
1
extend View::Cache::ClassMethods
-
-
1
attr_reader :format, :parent, :card
-
-
1
class << self
-
# @return [Symbol] viewname as Symbol
-
1
def normalize view
-
29550
view.present? ? view.to_sym : nil
-
end
-
-
# @return [Array] list of viewnames as Symbols
-
1
def normalize_list val
-
51784
case val
-
51593
when NilClass then []
-
191
when Array then val
-
when String then val.split(/[\s,]+/)
-
when Symbol then [val]
-
else raise Card::Error, "bad show/hide argument: #{val}"
-
end
-
end
-
end
-
-
# @param format [Card::Format]
-
# @param view [Symbol] viewname. Note: Card::View is initialized without a view
-
# when `voo` is called outside of a render,
-
# eg `subformat(cardname).method_with_voo_reference`.
-
# @param raw_options [Hash]
-
# @param parent [Card::View] (optional)
-
1
def initialize format, view, raw_options={}, parent=nil
-
23354
@format = format
-
23354
@raw_view = view
-
23354
@raw_options = raw_options
-
23354
@parent = parent
-
-
23354
@card = @format.card
-
23354
normalize_options
-
end
-
-
# handle rendering, including optional visibility, permissions, and caching
-
# @return [rendered view or a stub]
-
1
def process
-
23354
return if process_live_options == :hide
-
-
43696
fetch { yield ok_view }
-
end
-
-
# the view to "attempt". Typically the same as @raw_view, but @raw_view can
-
# be overridden, eg for the main view (top view of the main card on a page)
-
# @return [Symbol] view name
-
1
def requested_view
-
157817
@requested_view ||= View.normalize live_options[:view]
-
end
-
-
# the final view. can be different from @requested_view when there are
-
# issues with permissions, recursions, unknown cards, etc.
-
# @return [Symbol] view name
-
1
def ok_view
-
65544
@ok_view ||= format.monitor_depth { altered_view || requested_view }
-
end
-
-
# @return [Card::View]
-
1
def root
-
@root = parent ? parent.root : self
-
end
-
-
# @return [true/false]
-
1
def root?
-
!parent
-
end
-
-
# the root voo of the root format
-
1
def deep_root
-
format.root.voo
-
end
-
-
# neither view nor format has a parent
-
# @return [true/false]
-
1
def deep_root?
-
!parent && !format.parent
-
end
-
-
# next voo object found tracing ancestry through parent voos and/or parent formats
-
# @return [Card::View]
-
1
def next_ancestor across_format=true
-
74
parent || (across_format && next_format_ancestor) || nil
-
end
-
-
# voo object of format's parent
-
1
def next_format_ancestor
-
19597
format.parent&.voo
-
end
-
end
-
end
-
1
class Card
-
1
class View
-
# View::Cache supports smart card view caching.
-
#
-
# The basic idea is that when view caching is turned on (via `config.view_cache`),
-
# we try to cache a view whenever it's "safe" to do so. We will include everything
-
# inside that view (including other views) until we find something that isn't safe.
-
# When something isn't safe, we render a {Stub stub}: a placeholder
-
# with all the info we need to come back and replace it with the correct content
-
# later. In this way it is possible to have many levels of cached views within
-
# cached views.
-
#
-
# Here are some things that we never consider safe to cache:
-
#
-
# 1. a view explicitly configured _never_ to be cached
-
# 2. a view of a card with view-relevant permission restrictions
-
# 3. a view other than the requested view (eg a denial view)
-
# 4. a card with unsaved content changes
-
#
-
# We also consider it unsafe to cache a view of one card within a view of a different
-
# card, so nests are always handled with a stub.
-
#
-
# ## Cache configuration
-
#
-
# Cache settings (#5) can be configured in the
-
# {Set::Format::AbstractFormat#view view definition}
-
# and (less commonly) as a {Card::View::Options view option}.
-
#
-
# By far, the most common explicit caching configuration is `:never`. This setting
-
# is used to prevent over-caching, which becomes problematic when data changes
-
# do not clear the cache.
-
#
-
# Generally speaking, a card is smart about clearing its own view caches when
-
# anything about the card itself. So when I update the card "Johnny", all the cached
-
# views of "Johnny" are cleared. Similarly, changes to structure rules and other
-
# basic patterns are typically well managed by the caching system.
-
#
-
# However, there are many other potential changes that views cannot detect. Views that
-
# are susceptible to these "cache hazards" should be configured with `cache: :never`.
-
#
-
# ## Cache hazards
-
#
-
# If a view contains any of the following cache hazards, it would be wise to consider
-
# a `cache: :never` configuration:
-
#
-
# - dynamic searches (eg `Card.search`) whose results may change
-
# - live timestamps (eg `Time.now`)
-
# - environmental variables (eg `Env.params`)
-
# - any variables altered in one view and used in another (eg `@myvar`)
-
# - other cards' properties (eg `Card["random"].content`)
-
#
-
# What all of the above have in common is that they involve changes about which the
-
# view caching system is unaware. This means that whether the cache hazard is
-
# rendered directly in a view or just used in its logic, it can change in a way
-
# that _should_ change the view but _won't_ change the view if it's cached.
-
#
-
# ## Altering cached views
-
#
-
# Whereas ignoring cache hazards may cause over-caching, altering cached views
-
# may cause outright errors. If a view directly alters a rendered view,
-
# it may be dangerous to cache.
-
#
-
# # obviously safe to cache
-
# view(:x) { "ABC" }
-
#
-
# # also safe, because x is NOT altered
-
# view(:y) { render_x + "DEF" }
-
#
-
# # unsafe and thus never cached, because x is altered
-
# view(:z, cache: :never) { render_x.reverse }
-
#
-
# Specifically, the danger is that the inner view will be rendered as a stub,
-
# and the out view will end up altering the stub and not the view.
-
#
-
# Although alterations should be considered dangerous, they are actually only
-
# problematic in situations where the inner view might sometimes render a stub.
-
# If the outer view is rendering a view of the _same card_ with all the _same view
-
# settings_ (perms, unknown, etc), there will be no stub and thus no error.
-
# Remember, however, that a view on a narrow set may inherit view settings
-
# from a general set. To be confident that a view alteration is safe, all inherited
-
# settings must be taken into account.
-
#
-
# ## Caching Best Practices
-
#
-
# Here are some good rules of thumb to make good use of view caching:
-
#
-
# 1. *Use nests.* If you can show the content of a different card with a nest rather
-
# than by showing the content directly, the caching system will be much
-
# happier with you.
-
#
-
# view :bad_idea, cache: :never do
-
# Card["random"].content
-
# end
-
#
-
# view :good_idea do
-
# nest :random, view: :core
-
# end
-
#
-
# 2. *Isolate the cache hazards.* Consider the following variants:
-
#
-
# view :bad_idea, cache: :never do
-
# if morning_for_user?
-
# expensive_good_morning
-
# else
-
# expensive_good_afternoon
-
# end
-
# end
-
#
-
# view :good_idea, cache: :never do
-
# morning_for_user? ? render_good_morning : render_good_afternoon
-
# end
-
#
-
# In the first example, we have to generate expensive greetings every time we
-
# render the view. In the second, only the test is not cached.
-
#
-
# 3. If you must alter view results, consider *generating the view content
-
# in a separate method.*
-
#
-
# # First Attempt
-
#
-
# view :hash_it_in do
-
# { cool: false }
-
# end
-
#
-
# view :bad_idea, cache: :never do
-
# render_badhash.merge sucks: true
-
# end
-
#
-
#
-
# #Second Attempt
-
#
-
# view :hash_it_out do
-
# hash_it_out
-
# end
-
#
-
# def hash_it_out
-
# { cool: true }
-
# end
-
#
-
# view :good_idea do
-
# hash_it_out.merge rocks: true
-
# end
-
#
-
# The first attempt will work fine with caching off but is risky with caching on.
-
# The second is safe with caching on.
-
#
-
# ## Optimizing with `:always`
-
#
-
# It is never strictly necessary to use `cache: :always`, but this setting can help
-
# optimize your use of the caching system in some cases.
-
#
-
# Consider the following views:
-
#
-
# view(:hat) { "hat" } # ...but imagine this is computationally expensive
-
#
-
# view(:old_hat) { "old #{render_hat}" }
-
# view(:new_hat) { "new #{render_hat}" }
-
# view(:red_hat) { "red #{render_hat}" }
-
# view(:blue_hat) { "blue #{render_hat}" }
-
#
-
# Whether "hat" uses `:standard` or `:always`, the hat varieties (old, new, etc...)
-
# will fully contain the rendered hat view in their cache. However, with `:standard`,
-
# the other views will each re-render hat without attempting to cache it separately
-
# or to find it in the cache. This could lead to man expensive renderings of the
-
# "hat" view. By contrast, if the cache setting is `:always`, then hat will be
-
# cached and retrieved even when it's rendered inside another cached view.
-
#
-
1
module Cache
-
1
require "card/view/cache/cache_action"
-
1
require "card/view/cache/stub"
-
-
1
include CacheAction
-
1
include Stub
-
-
1
private
-
-
# render or retrieve view (or stub) with current options
-
# @param block [Block] code block to render
-
# @return [rendered view or stub]
-
1
def fetch &block
-
21848
case cache_action
-
21848
when :yield then yield # simple render
-
when :cache_yield then cache_render(&block) # render to/from cache
-
when :stub then stub # render stub
-
end
-
end
-
-
# Fetch view via cache and, when appropriate, render its stubs
-
#
-
# If this is a free cache action (see CacheAction), we go through the stubs and
-
# render them now.
-
# If the cache is active (ie, we are inside another view), we do not worry about
-
# stubs but keep going, because the free cache we're inside will take care of
-
# those stubs.
-
#
-
# @return [String (usually)] rendered view
-
1
def cache_render
-
cached_view = cache_fetch { yield }
-
cache_active? ? cached_view : format.stub_render(cached_view)
-
end
-
-
# Is there already a view cache in progress on which this one depends?
-
#
-
# Note that if you create a brand new independent format object
-
# (ie, not a subformat)
-
# its activity will be treated as unrelated to this caching/rendering.
-
#
-
# @return [true/false]
-
1
def cache_active?
-
deep_root? ? false : self.class.caching?
-
end
-
-
# If view is cached, retrieve it. Otherwise render and store it.
-
# Uses the primary cache API.
-
1
def cache_fetch
-
caching do
-
ensure_cache_key
-
self.class.cache.fetch cache_key do
-
yield
-
end
-
end
-
end
-
-
# keep track of nested cache fetching
-
1
def caching
-
self.class.caching(self) { yield }
-
end
-
-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
# VIEW CACHE KEY
-
-
1
def cache_key
-
@cache_key ||= [
-
card_cache_key, format.class, format.nest_mode, options_for_cache_key
-
].map(&:to_s).join "-"
-
end
-
-
1
def card_cache_key
-
card.real? ? card.id : "#{card.key}-#{card.type_id}"
-
end
-
-
# Registers the cached view for later clearing in the event of related card changes
-
1
def ensure_cache_key
-
card.ensure_view_cache_key cache_key
-
end
-
-
1
def options_for_cache_key
-
hash_for_cache_key(live_options) + hash_for_cache_key(viz_hash)
-
end
-
-
1
def hash_for_cache_key hash
-
hash.keys.sort.map do |key|
-
option_for_cache_key key, hash[key]
-
end.join ";"
-
end
-
-
1
def array_for_cache_key array
-
# TODO: needs better handling of edit_structure
-
# currently we pass complete structure as nested array
-
array.map do |item|
-
item.is_a?(Array) ? item.join(":") : item.to_s
-
end.sort.join ","
-
end
-
-
1
def option_for_cache_key key, value
-
"#{key}:#{option_value_to_string value}"
-
end
-
-
1
def option_value_to_string value
-
case value
-
when Hash then "{#{hash_for_cache_key value}}"
-
when Array then array_for_cache_key(value)
-
else value.to_s
-
end
-
end
-
-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
# cache-related Card::View class methods
-
1
module ClassMethods
-
1
def cache
-
Card::Cache[Card::View]
-
end
-
-
1
def caching?
-
!@caching.nil?
-
end
-
-
1
def caching voo
-
old_caching = @caching
-
@caching = voo
-
yield
-
ensure
-
@caching = old_caching
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class View
-
1
module Cache
-
# determine action to be used in #fetch
-
1
module CacheAction
-
1
private
-
-
# course of action based on config/status/options
-
# @return [Symbol] :yield, :cache_yield, or
-
1
def cache_action
-
21848
log_cache_action do
-
21848
send "#{cache_status}_cache_action"
-
end
-
end
-
-
1
def log_cache_action
-
21848
action = yield
-
# TODO: make configurable
-
# ...or better yet, integrate into performance logger...
-
# Rails.logger.warn "VIEW CACHE #{cache_active? ? '-->' : ''}[#{action}] "\
-
# "(#{card.name}##{requested_view})"
-
21848
action
-
end
-
-
# @return [Symbol] :off, :active, or :free
-
1
def cache_status
-
case
-
21848
when !cache_on?
-
21848
:off # view caching is turned off, format- or system-wide
-
when cache_active?
-
:active # another view cache is in progress (current view is inside it)
-
else
-
:free # no other cache in progress
-
end
-
end
-
-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
# CACHE STATUS: OFF
-
# view caching is turned off, format- or system-wide
-
-
# @return [True/False]
-
1
def cache_on?
-
21848
Card.config.view_cache && format.class.view_caching?
-
end
-
-
# always skip all the magic
-
1
def off_cache_action
-
21848
:yield
-
end
-
-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
# CACHE STATUS: FREE
-
# caching is on; no other cache in progress
-
-
# @return [Symbol]
-
1
def free_cache_action
-
free_cache_ok? ? :cache_yield : :yield
-
end
-
-
# @return [True/False]
-
1
def free_cache_ok?
-
cache_setting != :never && clean_enough_to_cache?
-
end
-
-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
# CACHE STATUS: ACTIVE
-
# another view cache is in progress; this view is inside it
-
-
# @return [Symbol]
-
1
def active_cache_action
-
active_cache_ok? ? active_cache_action_from_setting : :stub
-
end
-
-
# @return [True/False]
-
1
def active_cache_ok?
-
return false unless parent && clean_enough_to_cache?
-
return true if normalized_options[:skip_perms]
-
-
active_cache_permissible?
-
end
-
-
# apply any permission checks required by view.
-
# (do not cache views with nuanced permissions)
-
1
def active_cache_permissible?
-
case view_perms
-
when :none then true
-
when parent.view_perms then true
-
when Symbol then format.anyone_can?(view_perms)
-
else false
-
end
-
end
-
-
# determine the cache action from the cache setting
-
# (assuming cache status is "active")
-
# @return [Symbol] cache action
-
1
def active_cache_action_from_setting
-
level = ACTIVE_CACHE_LEVEL[cache_setting]
-
level || raise("unknown cache setting: #{cache_setting}")
-
end
-
-
ACTIVE_CACHE_LEVEL =
-
1
{ always: :cache_yield, standard: :yield, never: :stub }.freeze
-
-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
# SHARED METHODS
-
-
# @return [Symbol] :standard, :always, or :never
-
1
def cache_setting
-
format.view_cache_setting requested_view
-
end
-
-
# altered view requests and altered cards are not cacheable
-
# @return [True/False]
-
1
def clean_enough_to_cache?
-
# requested_view == ok_view && !card.unknown? && !card.db_content_changed?
-
requested_view == ok_view && card.view_cache_clean?
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class View
-
1
module Cache
-
# A "stub" is a placeholder for a card view.
-
#
-
# Cached views use stubs so that _nesting_ content can remained cached
-
# even while _nested_ content changes. The nested content's place is held
-
# by a stub.
-
#
-
# A stub must contain all the information necessary to produce the view as intended.
-
1
module Stub
-
1
private
-
-
# @return [String]
-
1
def stub
-
"(StUb#{stub_hash.to_json}sTuB)".html_safe
-
end
-
-
1
def bin_to_hex string
-
string.unpack("H*").first
-
end
-
-
# @return [Hash]
-
1
def stub_hash
-
{ cast: stub_cast,
-
view_opts: normalized_options.merge(normalized_visibility_options),
-
format_opts: { nest_mode: format.nest_mode,
-
override: root?,
-
context_names: format.context_names } }
-
# nest mode handling:
-
#
-
# Typically modes override views on nests, but stubs create non-standard nests.
-
# Mode-based view overrides should NOT apply to standard render calls that have
-
# been replaced with stubs - only to standard nest calls. The override value
-
# is used to retain this distinction.
-
end
-
-
1
def stub_cast
-
cast = card.cast
-
cast.delete :content if cast[:content].nil?
-
cast
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class View
-
# API to change css classes in other places
-
1
module Classy
-
# Add additional css classes to a css class
-
#
-
# Example
-
# class_up "card-slot", "card-dark text-muted"
-
#
-
# If a view later adds the css "card-slot" to a html tag with
-
#
-
# classy("card-slot")
-
#
-
# then all additional css classes will be added.
-
#
-
# The scope when these additional classes apply can be restricted
-
# @param klass [String, Symbol] the css class to be enriched with additional classes
-
# @param classier [String, Array<String>] additional css classes
-
# @param scope [Symbol]
-
# :view only in the same view
-
# :subviews the same and all subviews; not in nests or where its nested
-
# :format all views, sub and parent views; not in nests or where its nested
-
# :nests the same as :format but also in nests
-
# :single_use the same as :nests but is removed after the first use
-
# :global always everywhere
-
1
def class_up klass, classier, scope=:subviews
-
1349
klass = klass.to_s
-
-
1349
storage_voo(scope).add_extra_classes klass, classier, scope
-
end
-
-
1
def class_down klass, classier
-
74
remove_extra_classes klass, classier, :private
-
end
-
-
1
def with_class_up klass, classier, scope=:subviews
-
74
class_up klass, classier, scope
-
74
yield
-
ensure
-
74
class_down klass, classier
-
end
-
-
# don't use in the given block the additional class that
-
# was added to `klass`
-
1
def without_upped_class klass
-
tmp_class = class_list.delete klass
-
result = yield tmp_class
-
class_list[klass] = tmp_class
-
result
-
end
-
-
1
def classy *classes
-
7814
classes = Array.wrap(classes).flatten
-
7814
[classes, extra_classes(classes)].flatten.compact.join " "
-
end
-
-
1
def add_extra_classes key, classier, scope
-
1349
type = class_list_type scope
-
-
1349
class_list(type)[key] =
-
[class_list(type)[key], classier].flatten.compact.join(" ")
-
end
-
-
# remove classes everywhere where they are visible for the given scope
-
1
def remove_extra_classes klass, classier, type
-
# TODO: scope handling
-
# Method is not used and maybe no longer necessary with the scope feature
-
# for class_up.
-
-
# It's no longer sufficient to remove only public classes for ancestors.
-
# Needs an approach similar to extra_classes with the "space" argument
-
74
next_ancestor&.remove_extra_classes klass, classier, :public
-
-
74
cl = class_list type
-
74
return unless cl[klass]
-
-
if cl[klass] == classier
-
cl.delete klass
-
else
-
cl[klass].gsub!(/#{classier}\s?/, "")
-
end
-
end
-
-
1
def extra_classes klass
-
7814
klass = klass.first if klass.is_a?(Array)
-
7814
klass = klass.to_s
-
-
7814
deep_extra_classes klass, :self
-
end
-
-
# recurse through voos and formats to find all extra classes
-
# @param space [:self, :self_format, :ancestor_format]
-
1
def deep_extra_classes klass, space
-
36738
[self_extra_classes(klass, space),
-
ancestor_extra_classes(klass, space)].flatten.compact
-
end
-
-
1
private
-
-
1
def ancestor_extra_classes klass, space
-
36738
if parent
-
17215
parent_space = space == :self ? :self_format : :ancestor_format
-
17215
parent.deep_extra_classes(klass, parent_space)
-
else
-
19523
next_format_ancestor&.deep_extra_classes(klass, :ancestor_format)
-
end
-
end
-
-
1
def storage_voo scope
-
# When we climb up the voo tree and cross a nest boundary then we can jump only
-
# to the root voo of the parent format. Hence we have to add classes to the root
-
# if we want them to be found by nests.
-
1349
case scope
-
1349
when :view, :subviews then self
-
when :format, :nests, :single_use then root
-
when :global then deep_root
-
else
-
raise ArgumentError, "invalid class_up scope: #{scope}"
-
end
-
end
-
-
1
def self_extra_classes klass, space
-
95087
classes = ok_types(space).map { |ot| class_list(ot)[klass] }
-
36738
return classes unless class_list(:single_use)&.key? klass
-
-
[classes, class_list(:single_use).delete(klass)]
-
end
-
-
1
def ok_types space
-
36738
case space
-
22941
when :ancestor_format then [:public]
-
5983
when :self_format then %i[public format_private]
-
7814
when :self then %i[public format_private private]
-
end
-
end
-
-
1
def class_list type=:private
-
97859
case type
-
when :private, :format_private, :public, :single_use
-
97859
@class_list ||= {}
-
97859
@class_list[type] ||= {}
-
else
-
raise ArgumentError, "#{type} not a valid class list"
-
end
-
end
-
-
# Translates scopes to the privacy types used to manage the class lists.
-
# A #classy calls looks in the following class_lists:
-
# private - only in the same voo
-
# format_private - the same voo and all parent voos in the same format
-
# public - in all voos in all parent formats
-
1
def class_list_type scope
-
1349
case scope
-
when :view
-
:private
-
when :format, :subviews
-
1349
:format_private
-
when :nests, :global
-
:public
-
when :single_use
-
:single_use
-
else
-
raise ArgumentError, "invalid class_up scope: #{scope}"
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class View
-
# Manages options for rendering card views
-
#
-
# Many options are available to sharks via nests. (See https://decko.org/Nest_Syntax)
-
#
-
# {{cardname|hide:menu}}
-
#
-
# These options and others are available to monkeys when rendering views
-
# via #render or #nest.
-
#
-
# nest "cardname", hide: :menu
-
# render :viewname, hide: :menu
-
#
-
1
module Options
-
# the keymap represents a 2x2 matrix, where the factors are
-
# (a) whether an option's value can be set by a shark via nests, and
-
# (b) whether subviews can inherit the option from a parent view.
-
#
-
# for sharks | not for sharks
-
# ________________________________
-
# inherit | both | heir
-
# don't inherit | shark | none
-
#
-
# (note: each option will likely some day merit its own object)
-
@keymap = {
-
1
shark: [
-
:view, # view to render
-
:nest_name, # name as used in nest
-
:nest_syntax, # full nest syntax
-
:wrap, # wrap the nest with a wrapper
-
:show, # render these views when optional
-
:hide # do not render these views when optional
-
], # show/hide can be view (Symbol), list of views (Array),
-
# or comma separated views (String)
-
# NOTE: although show and hide are in this non-inheriting group, they are
-
# actually inherited, just not through the standard mechanism. Because, well,
-
# they're weird. (See process_visibility)
-
heir: [
-
:main, # format object is page's "main" object (Boolean)
-
:home_view, # view for slot to return to when no view specified
-
:edit_structure, # use a different structure for editing (Array)
-
:cql, # contextual cql alterations for search cards (Hash)
-
:action_id, # a Card::Action id (Integer)
-
:content_opts # options for Card::Content.new
-
# :context_names # names used to contextualize titles
-
],
-
both: [
-
:help, # cue text when editing
-
:structure, # overrides the content of the card
-
:title, # overrides the name of the card
-
:variant, # override the canonical version of the name with a different
-
# variant
-
:input_type, # inline_nests makes a form within standard content (Symbol)
-
:type, # set the default type of new cards
-
:size, # set an image size
-
# (also used for character limit in one_line_content)
-
:params, # parameters for add button. deprecated!
-
:items, # options for items (Hash)
-
:cache, # change view cache behaviour
-
# (Symbol<:always, :standard, :never>)
-
:edit, # edit mode
-
# (Symbol<:inline, :standard, :full>)
-
:separator, # item separator in certain lists
-
:filter
-
],
-
none: [
-
:skip_perms, # do not check permissions for this view (Boolean)
-
:main_view, # this is main view of page (Boolean)
-
:layout #
-
]
-
}
-
# Note: option values are strings unless otherwise noted
-
-
1
class << self
-
1
attr_reader :keymap
-
-
1
def add_option name, type
-
raise "invalid option type: #{type}" unless @keymap.key?(type)
-
-
@keymap[type] << name
-
reset_key_lists
-
VooApi.define_getter name
-
VooApi.define_setter name
-
end
-
end
-
-
1
extend KeyLists
-
1
include VooApi
-
1
include Visibility
-
end
-
end
-
end
-
1
class Card
-
1
class View
-
1
module Options
-
1
module KeyLists
-
# all standard option keys
-
# @return [Array]
-
1
def all_keys
-
73417
@all_keys ||= keymap.each_with_object([]) { |(_k, v), a| a.push(*v) }
-
end
-
-
# keys whose values can be set by Sharks in card nests
-
# @return [Array]
-
1
def shark_keys
-
3864
@shark_keys ||= ::Set.new(keymap[:both]) + keymap[:shark]
-
end
-
-
# keys that follow simple standard inheritance pattern from parent views
-
# @return [Array]
-
1
def heir_keys
-
16732
@heir_keys ||= ::Set.new(keymap[:both]) + keymap[:heir]
-
end
-
-
# Keys that can be read or written via accessors
-
# @return [Array]
-
1
def accessible_keys
-
1
all_keys - [ # (all but the following)
-
:view, # view is accessed as requested_view or ok_view and cannot be
-
# directly manipulated
-
:show, :hide # these have a more extensive API (see Card::View::Visibility)
-
]
-
end
-
-
1
def slot_keys
-
1516
@slot_keys ||= all_keys - [:skip_perms]
-
end
-
-
1
def reset_key_lists
-
@all_keys = nil
-
@shark_keys = nil
-
@heir_keys = nil
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class View
-
1
module Options
-
# manages showing and hiding optional view renders
-
1
module Visibility
-
# tracks show/hide value for each view with an explicit setting
-
# eg { toggle: :hide }
-
1
def viz_hash
-
138081
@viz_hash ||= {}
-
end
-
-
# test methods
-
-
1
def hide? view
-
24511
viz_hash[view&.to_sym] == :hide
-
end
-
-
1
def show? view
-
1319
!hide? view
-
end
-
-
# write methods
-
-
1
def show *views
-
94
viz views, :show
-
end
-
-
1
def hide *views
-
954
viz views, :hide
-
end
-
-
# force write methods
-
-
1
def show! *views
-
1
viz views, :show, true
-
end
-
-
1
def hide! *views
-
118
viz views, :hide, true
-
end
-
-
# advanced write method
-
1
VIZ_SETTING = { show: :show, true => :show,
-
hide: :hide, false => :hide, nil => :hide }.freeze
-
-
1
def viz views, setting, force=false
-
74934
Array.wrap(views).flatten.each do |view|
-
23833
view = view.to_sym
-
23833
next if !force && viz_hash[view]
-
-
23569
viz_hash[view] = VIZ_SETTING[setting]
-
end
-
end
-
-
1
def visible? view
-
172
viz view, yield unless viz_hash[view]
-
172
show? view
-
end
-
-
# test whether view is optional
-
# (@optional is set in normalize_options
-
# @return [true/false]
-
1
def optional?
-
23354
@optional
-
end
-
-
# translate raw hide, show options (which can be strings, symbols,
-
# arrays, etc)
-
1
def process_visibility
-
25892
viz_hash.reverse_merge! parent.viz_hash if parent
-
25892
process_visibility_options live_options
-
25892
viz requested_view, @optional if @optional && !viz_hash[requested_view]
-
end
-
-
1
private
-
-
# if true, #process returns nil
-
1
def hide_requested_view?
-
23354
optional? && hide?(requested_view)
-
end
-
-
# takes an options_hash and processes it to update viz_hash
-
1
def process_visibility_options options_hash
-
25892
%i[hide show].each do |setting|
-
51784
views = View.normalize_list(options_hash.delete(setting)).map(&:to_sym)
-
51784
viz views, setting, true
-
end
-
end
-
-
1
def normalized_visibility_options
-
viz_hash.each_with_object({}) do |(key, val), hash|
-
hash[val] ||= []
-
hash[val] << key
-
end
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class View
-
1
module Options
-
# VooApi methods let developers use view options dynamically.
-
1
module VooApi
-
# There are two primary options hashes:
-
-
# - @normalized_options are determined upon initialization and do not change
-
# after that.
-
# @return [Hash] options
-
1
attr_reader :normalized_options
-
-
1
class << self
-
1
def included base
-
# Developers can also set most options directly via accessors,
-
# eg voo.title = "King"
-
# :view, :show, and :hide have non-standard access (see #accessible_keys)
-
-
1
base.accessible_keys.each do |option_key|
-
25
define_getter option_key unless option_key == :items
-
25
define_setter option_key
-
end
-
end
-
-
1
def define_getter option_key
-
24
define_method option_key do
-
60171
live_options[option_key]
-
end
-
end
-
-
1
def define_setter option_key
-
25
define_method "#{option_key}=" do |value|
-
728
live_options[option_key] = special_option_value(option_key, value) || value
-
end
-
end
-
end
-
-
# "items", the option used to configure views of each of a list of cards, is
-
# currently the only Hash option (thus this accessor override)
-
# @return [Hash]
-
1
def items
-
255
live_options[:items] ||= {}
-
end
-
-
# options to be used in data attributes of card slots (normalized options
-
# with standard keys)
-
# FIXME: what we really want is options as they were when render was called.
-
# normalized is wrong because it can get changed before render. live is wrong
-
# because they can get changed after. current solution is a compromise.
-
# @return [Hash]
-
1
def slot_options
-
1200
normalized_options.merge(view: requested_view).slice(*Options.slot_keys)
-
end
-
-
# ACCESSOR_HELPERS
-
# methods that follow the normalize_#{key} pattern are called by accessors
-
# (arguably that should be done during normalization!)
-
-
1
def normalize_special_options! opts
-
23354
opts.each do |option_key, value|
-
55134
new_value = special_option_value option_key, value
-
55134
opts[option_key] = new_value if new_value
-
end
-
end
-
-
1
def special_option_value option_key, value
-
55862
try "normalize_#{option_key}", value
-
end
-
-
1
def normalize_input_type value
-
value&.to_sym
-
end
-
-
1
def normalize_edit value
-
246
value&.to_sym
-
end
-
-
1
def normalize_cache value
-
value&.to_sym
-
end
-
-
1
def normalize_wrap value
-
224
value = value.split(",").map(&:strip) if value.is_a? String
-
224
Array.wrap(value).compact.flatten
-
end
-
-
1
protected
-
-
# - @live_options are dynamic and can be altered by the "voo" API at any time.
-
# Such alterations are NOT used in the stub of the current voo, but they
-
# ARE inherited by children voos.
-
#
-
# @return [Hash]
-
1
def live_options
-
428308
@live_options ||= process_live_options
-
end
-
-
1
private
-
-
# option normalization includes standardizing options into a hash with
-
# symbols as keys, managing standard view inheritance, and special
-
# handling for main_views.
-
1
def normalize_options
-
23354
@normalized_options = opts = options_to_hash @raw_options.clone
-
23354
normalize_special_options! opts
-
23354
@optional = opts.delete(:optional) || false
-
23354
add_implicit_options!
-
23354
inherit_options_from_parent!
-
23354
validate_options! opts
-
23354
opts
-
end
-
-
1
def add_implicit_options!
-
23354
@normalized_options[:view] = @raw_view
-
23354
@normalized_options[:main] = true if format.main?
-
# opts[:context_names] = format.context_names
-
end
-
-
# typically options are already a hash. this also handles an array of
-
# hashes and nil.
-
1
def options_to_hash opts
-
23354
case opts
-
when ActionController::Parameters
-
opts.to_unsafe_h.deep_symbolize_keys
-
23354
when Hash then opts.deep_symbolize_keys!
-
when Array then opts[0].merge(opts[1]).deep_symbolize_keys!
-
when nil then {}
-
else raise Card::Error, "bad view options: #{opts}"
-
end
-
end
-
-
# standard inheritance from parent view object
-
1
def inherit_options_from_parent!
-
23354
return unless parent
-
-
16732
Options.heir_keys.each do |option_key|
-
317908
inherit_from_parent! option_key
-
end
-
end
-
-
1
def inherit_from_parent! option_key
-
317908
return unless (parent_value = parent.live_options[option_key])
-
-
23843
@normalized_options[option_key] ||= parent_value
-
end
-
-
1
def process_live_options
-
23354
@live_options = normalized_options.clone
-
23354
process_main_nest_options
-
23354
process_before_view
-
23354
process_visibility
-
23354
return :hide if hide_requested_view? # bail to avoid unnecessary processing
-
-
21848
process_view_wrappers
-
21848
@live_options
-
end
-
-
# This method triggers the "before" blocks which can alter the @live_options
-
# hash both directly and indirectly (via the voo API)
-
1
def process_before_view
-
23354
format.before_view requested_view
-
end
-
-
# adds the wrappers assigned to ok_view in view definition
-
1
def process_view_wrappers
-
21848
view_wrappers = format.view_setting(:wrap, ok_view)
-
21848
return unless view_wrappers.present?
-
-
628
@live_options[:wrap] = Array.wrap(@live_options[:wrap])
-
628
if view_wrappers.is_a? ::Hash
-
127
view_wrappers.each_pair do |name, opts|
-
127
@live_options[:wrap] << [name, opts]
-
end
-
else
-
501
@live_options[:wrap] += Array.wrap(view_wrappers)
-
end
-
end
-
-
# merge the options of the main nest into the @live_options
-
# They are not processed in normalize_options so that
-
# they're NOT locked in the stub.
-
1
def process_main_nest_options
-
23354
@live_options.merge! format.main_nest_options if @live_options[:main_view]
-
end
-
-
1
def validate_options! opts
-
23354
return unless (foreign_opts = foreign_options_in opts)
-
-
# TODO: this should raise a UserError if the options come directly from params
-
# (eg, mycard?slot[badoption]=true should not be treated as a server error)
-
raise Card::Error, "illegal view options: #{foreign_opts}"
-
end
-
-
# find non-standard option in Hash
-
# @param opts [Hash] options hash
-
# @return [Hash] options Hash
-
1
def foreign_options_in opts
-
96765
foreign_opts = opts.reject { |k, _v| Options.all_keys.include? k }
-
23354
foreign_opts.empty? ? nil : foreign_opts
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class View
-
# View permissions support view-specific permission handling
-
#
-
# Views can be configured in {Set::Format::AbstractFormat#view view definitions}
-
# with the `perms` directive, eg
-
#
-
# # only render if user has permission to update card
-
# view :myview, perms: :update do...
-
1
module Permission
-
1
def view_perms
-
32881
@view_perms = setting(:perms) || :read
-
end
-
-
1
private
-
-
1
def altered_view
-
21848
return if skip_check?
-
8375
alter_unknown || denial
-
end
-
-
1
def skip_check?
-
21848
normalized_options[:skip_perms] || view_perms == :none
-
end
-
-
1
def setting setting_name, view=nil
-
41256
view ||= requested_view
-
41256
format.view_setting setting_name, view
-
end
-
-
# by default views can't handle unknown cards, but this can be overridden in
-
# view definitions with the `unknown` directive
-
1
def alter_unknown
-
8375
setting = setting(:unknown)
-
8375
return if setting == true || card.known?
-
29
setting.is_a?(Symbol) ? setting : format.view_for_unknown(requested_view)
-
end
-
-
1
def denial
-
8346
return unless (task = denied_task)
-
-
527
format.view_for_denial requested_view, task
-
end
-
-
1
def denied_task
-
8346
if view_perms.is_a? Proc
-
982
:read unless view_perms.call(format) # read isn't quite right
-
else
-
14728
Array.wrap(view_perms).find { |task| !format.ok? task }
-
end
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
1
class Content
-
1
module Chunk
-
# These are basic chunks that have a pattern and can be protected.
-
# They are used by rendering process to prevent wiki rendering
-
# occuring within literal areas such as <code> and <pre> blocks
-
# and within HTML tags.
-
1
class EscapedLiteral < Abstract
-
1
FULL_RE = { "[" => /\A\\\[\[[^\]]*\]\]/,
-
"{" => /\A\\\{\{[^\}]*\}\}/ }.freeze
-
1
Card::Content::Chunk.register_class self,
-
prefix_re: '\\\\(?:\\[\\[|\\{\\{)',
-
idx_char: '\\'
-
-
1
def self.full_re prefix
-
8
FULL_RE[prefix[1, 1]]
-
end
-
-
1
def interpret match, _content
-
8
@process_chunk = match[0].sub(/^\\(.)/, format.escape_literal('\1'))
-
end
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
1
class Content
-
1
module Chunk
-
# These are basic chunks that have a pattern and can be protected.
-
# This chunk is used for markdown processing to ensure that
-
# the escaping survives the markdown rendering.
-
1
class KeepEscapedLiteral < Abstract
-
1
FULL_RE = { "[" => /\A\\\[\[[^\]]*\]\]/,
-
"{" => /\A\\\{\{[^\}]*\}\}/ }.freeze
-
1
Card::Content::Chunk.register_class self,
-
prefix_re: '\\\\(?:\\[\\[|\\{\\{)',
-
idx_char: '\\'
-
-
1
def self.full_re prefix
-
8
FULL_RE[prefix[1, 1]]
-
end
-
-
1
def interpret match, _content
-
8
@process_chunk = match[0].sub(/^\\(.)/, '\\\\\\\\\1')
-
end
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
# require File.expand_path("../reference", __FILE__)
-
1
load File.expand_path("../reference.rb", __FILE__)
-
-
1
class Card
-
1
class Content
-
1
module Chunk
-
# extend ActiveSupport::Autoload
-
# autoload :Reference , "reference"
-
-
1
class Link < Card::Content::Chunk::Reference
-
1
CODE = "L".freeze # L for "Link"
-
1
attr_reader :link_text
-
# Groups: $1, [$2]: [[$1]] or [[$1|$2]] or $3, $4: [$3][$4]
-
1
Card::Content::Chunk.register_class self,
-
prefix_re: '\\[\\[',
-
full_re: /\A\[\[([^\]]+)\]\]/,
-
idx_char: "["
-
1
def reference_code
-
10
CODE
-
end
-
-
1
def interpret match, _content
-
target, @link_text =
-
2080
if (raw_syntax = match[1])
-
2080
if (i = divider_index(raw_syntax)) # [[A | B]]
-
1718
[raw_syntax[0..(i - 1)], raw_syntax[(i + 1)..-1]]
-
else # [[ A ]]
-
362
[raw_syntax, nil]
-
end
-
end
-
-
2080
@link_text = objectify @link_text
-
2080
if target.match? %r{^(/|https?:|mailto:)}
-
724
@explicit_link = objectify target
-
else
-
1356
@name = target
-
end
-
end
-
-
1
def divider_index string
-
# there's probably a better way to do the following.
-
# point is to find the first pipe that's not inside an nest
-
2080
return unless string.index "|"
-
1718
string_copy = string.dup
-
1718
string.scan(/\{\{[^\}]*\}\}/) do |incl|
-
string_copy.gsub! incl, ("x" * incl.length)
-
end
-
1718
string_copy.index "|"
-
end
-
-
# view options
-
1
def options
-
1233
link_text ? { title: link_text } : {}
-
end
-
-
1
def objectify raw
-
2804
return unless raw
-
2442
raw.strip!
-
2442
if raw =~ /(^|[^\\])\{\{/
-
Card::Content.new raw, format
-
else
-
2442
raw
-
end
-
end
-
-
1
def render_link view: :link, explicit_link_opts: {}
-
1942
@link_text = render_obj @link_text
-
-
1942
if @explicit_link
-
709
@explicit_link = render_obj @explicit_link
-
709
format.link_to_resource @explicit_link, @link_text, explicit_link_opts
-
1233
elsif @name
-
1233
format.with_nest_mode :normal do
-
1233
format.nest referee_name, options.merge(view: view)
-
end
-
end
-
end
-
-
1
def link_target
-
if @explicit_link
-
render_obj @explicit_link
-
elsif @name
-
referee_name
-
end
-
end
-
-
1
def process_chunk
-
150
@process_chunk ||= render_link
-
end
-
-
1
def inspect
-
"<##{self.class}:e[#{@explicit_link}]n[#{@name}]l[#{@link_text}]" \
-
"p[#{@process_chunk}] txt:#{@text}>"
-
end
-
-
1
def replace_reference old_name, new_name
-
replace_name_reference old_name, new_name
-
replace_link_text old_name, new_name
-
@text =
-
@link_text.nil? ? "[[#{referee_name}]]" : "[[#{referee_name}|#{@link_text}]]"
-
end
-
-
1
def replace_link_text old_name, new_name
-
if @link_text.is_a?(Card::Content)
-
@link_text.find_chunks(Card::Content::Chunk::Reference).each do |chunk|
-
chunk.replace_reference old_name, new_name
-
end
-
elsif @link_text.present?
-
@link_text = old_name.to_name.sub_in(@link_text, with: new_name)
-
end
-
end
-
-
1
def explicit_link?
-
1792
@explicit_link
-
end
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
# require File.expand_path("../reference", __FILE__)
-
-
1
class Card
-
1
class Content
-
1
module Chunk
-
# Handler for nest chunks: {{example}}
-
1
class Nest < Reference
-
1
attr_reader :options
-
1
DEFAULT_OPTION = :view # a value without a key is interpreted as view
-
-
1
Chunk.register_class(self, prefix_re: '\\{\\{',
-
full_re: /\A\{\{([^\{\}]*)\}\}/,
-
idx_char: "{")
-
-
1
def interpret match, _content
-
3881
in_brackets = strip_tags match[1]
-
3881
name, @opt_lists = in_brackets.split "|", 2
-
3881
name = name.to_s.strip
-
3881
if name =~ /^\#/
-
15
@process_chunk = name =~ /^\#\#/ ? "" : visible_comment(in_brackets)
-
else
-
3866
@options = interpret_options.merge nest_name: name,
-
nest_syntax: in_brackets
-
3866
@name = name
-
end
-
end
-
-
1
def strip_tags string
-
# note: not using ActionView's strip_tags here
-
# because this needs to be super fast.
-
3881
string.gsub(/\<[^\>]*\>/, "")
-
end
-
-
1
def visible_comment message
-
"<!-- #{CGI.escapeHTML message} -->"
-
end
-
-
1
def interpret_options
-
3866
raw_options = @opt_lists.to_s.split("|").reverse
-
3866
raw_options.inject(nil) do |prev_level, level_options|
-
3640
interpret_piped_options level_options, prev_level
-
end || {}
-
end
-
-
1
def interpret_piped_options list_string, items
-
3640
options_hash = items.nil? ? {} : { items: items }
-
3640
option_string_to_hash list_string, options_hash
-
3640
options_hash
-
end
-
-
1
def option_string_to_hash list_string, options_hash
-
3640
each_option(list_string) do |key, value|
-
3864
key = key.to_sym
-
3864
if key == :item
-
options_hash[:items] ||= {}
-
options_hash[:items][:view] = value
-
3864
elsif Card::View::Options.shark_keys.include? key
-
3864
options_hash[key] = value
-
# else
-
# handle other keys
-
end
-
end
-
end
-
-
1
def inspect
-
"<##{self.class}:n[#{@name}] p[#{@process_chunk}] txt:#{@text}>"
-
end
-
-
1
def process_chunk
-
3661
return @process_chunk if @process_chunk
-
-
3646
referee_name
-
3646
@processed = format.content_nest(@options)
-
# this is not necessarily text, sometimes objects for json
-
end
-
-
1
def replace_reference old_name, new_name
-
replace_name_reference old_name, new_name
-
nest_body = [@name.to_s, @opt_lists].compact * "|"
-
@text = "{{#{nest_body}}}"
-
end
-
-
1
def explicit_view= view
-
return if @options[:view]
-
# could check to make sure it's not already the default...
-
if @text =~ /\|/
-
@text.sub! "|", "|#{view};"
-
else
-
@text.sub! "}}", "|#{view}}}"
-
end
-
end
-
-
1
def main?
-
4
nest_name == "_main"
-
end
-
-
1
def nest_name
-
4
options&.dig :nest_name
-
end
-
-
1
def self.gsub string
-
50
string.gsub(/\{\{([^\}]*)\}\}/) do |_match|
-
143
yield(Regexp.last_match[1])
-
end
-
end
-
-
1
def raw_options
-
@opt_lists
-
end
-
-
1
private
-
-
1
def each_option attr_string
-
3640
return if attr_string.blank?
-
3640
attr_string.strip.split(";").each do |pair|
-
# key is optional for view option
-
3864
value, key = pair.split(":", 2).reverse
-
3864
key ||= self.class::DEFAULT_OPTION.to_s
-
3864
yield key.strip, value.strip
-
end
-
end
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
1
class Content
-
1
module Chunk
-
# This should find +Alfred+ in expressions like
-
# 1) {"name":"Alfred"}
-
# 2a) {"name":["in","Alfred"]}
-
# 3a) {"plus_right":["Alfred"]}
-
# but not in
-
# 2b) "content":"foo", "Alfred":"bar"
-
# 3b) {"name":["Alfred", "Toni"]} ("Alfred" is an operator here)
-
# It's not possible to distinguish between 2a) and 2b) or 3a) and 3b) with a
-
# simple regex, hence we use a too general regex and check for query keywords
-
# after the match, which of course means that we don't find references with
-
# query keywords as name
-
-
1
require File.expand_path("reference", __dir__)
-
1
class QueryReference < Reference
-
1
QUERY_KEYWORDS = ::Set.new(
-
(
-
1
::Card::Query::MODIFIERS.keys +
-
::Card::Query::OPERATORS.keys +
-
::Card::Query::ATTRIBUTES.keys +
-
::Card::Query::CONJUNCTIONS.keys +
-
%w[desc asc count]
-
).map(&:to_s)
-
)
-
-
1
Card::Content::Chunk.register_class(
-
self, prefix_re: '(?<=[:,\\[])\\s*"',
-
# we check for colon, comma or square bracket before a quote
-
# we have to use a lookbehind, otherwise
-
# if the colon matches it would be
-
# identified mistakenly as an URI chunk
-
full_re: /\A\s*"([^"]+)"/,
-
idx_char: '"'
-
)
-
-
# OPTIMIZE: instead of comma or square bracket check for operator followed
-
# by comma or "plus_right"|"plus_left"|"plus" followed by square bracket
-
# something like
-
# prefix_patterns = [
-
# "\"\\s*(?:#{Card::Query::OPERATORS.keys.join('|')})\"\\s*,",
-
# "\"\\s*(?:#{Card::Query::PLUS_ATTRIBUTES}.keys
-
# .join('|')})\\s*:\\s*\\[\\s*",
-
# "\"\\s*(?:#{(QUERY_KEYWORDS - Card::Query::PLUS_ATTRIBUTES)
-
# .join('|')})\"\\s*:",
-
# ]
-
# prefix_re: '(?<=#{prefix_patterns.join('|')})\\s*"'
-
# But: What do we do with the "in" operator? After the first value there is
-
# no prefix which we can use to detect the following values as
-
# QueryReference chunks
-
-
1
class << self
-
1
def full_match content, prefix
-
# matches cardnames that are not keywords
-
# FIXME: would not match cardnames that are keywords
-
64
match, offset = super(content, prefix)
-
64
return if !match || keyword?(match[1])
-
-
64
[match, offset]
-
end
-
-
1
def keyword? str
-
64
return unless str
-
-
64
QUERY_KEYWORDS.include?(str.tr(" ", "_").downcase)
-
end
-
end
-
-
1
def interpret match, _content
-
64
@name = match[1]
-
end
-
-
1
def process_chunk
-
@process_chunk ||= @text
-
end
-
-
1
def inspect
-
"<##{self.class}:n[#{@name}] p[#{@process_chunk}] txt:#{@text}>"
-
end
-
-
1
def replace_reference old_name, new_name
-
replace_name_reference old_name, new_name
-
@text = "\"#{@name}\""
-
end
-
-
1
def reference_code
-
64
"Q" # for "Query"
-
end
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
1
class Content
-
1
module Chunk
-
1
class Reference < Abstract
-
1
attr_accessor :referee_name, :name
-
-
1
def referee_name
-
5394
return if name.nil?
-
5394
@referee_name ||= referee_name_from_rendered(render_obj(name))
-
5394
@referee_name = @referee_name.absolute(card.name).to_name
-
rescue Card::Error::NotFound
-
# do not break on missing id/codename references.
-
end
-
-
1
def referee_name_from_rendered rendered_name
-
5160
ref_card = fetch_referee_card rendered_name
-
5160
ref_card ? ref_card.name : rendered_name.to_name
-
end
-
-
1
def referee_card
-
51
@referee_card ||= referee_name && Card.fetch(referee_name)
-
end
-
-
1
def replace_name_reference old_name, new_name
-
@referee_card = nil
-
@referee_name = nil
-
if name.is_a? Card::Content
-
name.find_chunks(Chunk::Reference).each do |chunk|
-
chunk.replace_reference old_name, new_name
-
end
-
else
-
@name = name.to_name.swap old_name, new_name
-
end
-
end
-
-
1
def render_obj raw
-
7811
if format && raw.is_a?(Card::Content)
-
format.process_content raw
-
else
-
7811
raw
-
end
-
end
-
-
1
private
-
-
1
def fetch_referee_card rendered_name
-
5160
case rendered_name # FIXME: this should be standard fetch option.
-
when /^\~(\d+)$/ # get by id
-
Card.fetch Regexp.last_match(1).to_i
-
when /^\:(\w+)$/ # get by codename
-
234
Card.fetch Regexp.last_match(1).to_sym
-
end
-
end
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
require "uri"
-
-
# This wiki chunk matches arbitrary URIs, using patterns from the Ruby URI
-
# modules.
-
# It parses out a variety of fields that could be used by formats to format
-
# the links in various ways (shortening domain names, hiding email addresses)
-
# It matches email addresses and host.com.au domains without schemes (http://)
-
# but adds these on as required.
-
#
-
# The heuristic used to match a URI is designed to err on the side of caution.
-
# That is, it is more likely to not autolink a URI than it is to accidently
-
# autolink something that is not a URI. The reason behind this is it is easier
-
# to force a URI link by prefixing 'http://' to it than it is to escape and
-
# incorrectly marked up non-URI.
-
#
-
# I'm using a part of the [ISO 3166-1 Standard][iso3166] for country name
-
# suffixes.
-
# The generic names are from www.bnoack.com/data/countrycode2.html)
-
# [iso3166]: http://geotags.com/iso3166/
-
1
module Card::Content::Chunk
-
1
class URI < Abstract
-
1
SCHEMES = %w[irc http https ftp ssh git sftp file ldap ldaps mailto].freeze
-
-
6
REJECTED_PREFIX_RE = %w{! ": " ' ](}.map { |s| Regexp.escape s } * "|"
-
-
1
attr_reader :uri, :link_text
-
1
delegate :to, :scheme, :host, :port, :path, :query, :fragment, to: :uri
-
-
1
Card::Content::Chunk.register_class(
-
self, prefix_re: "(?:(?!#{REJECTED_PREFIX_RE})(?:#{SCHEMES * '|'})\\:)",
-
full_re: /\A#{::URI.regexp(SCHEMES)}/,
-
idx_char: ":"
-
)
-
-
1
class << self
-
1
def full_match content, prefix
-
6
prepend_str = if prefix[-1, 1] != ":" && config[:prepend_str]
-
6
config[:prepend_str]
-
else
-
""
-
end
-
6
content = prepend_str + content
-
6
match = super content, prefix
-
6
[match, prepend_str.length]
-
end
-
-
1
def context_ok? content, chunk_start
-
6
preceding_string = content[chunk_start - 2..chunk_start - 1]
-
6
preceding_string !~ /(?:#{REJECTED_PREFIX_RE})$/
-
end
-
end
-
-
1
def interpret match, _content
-
6
chunk = match[0]
-
6
last_char = chunk[-1, 1]
-
6
chunk.gsub!(/(?: )+/, "")
-
-
@trailing_punctuation =
-
6
if %w[, . ) ! ? :].member?(last_char)
-
@text.chop!
-
chunk.chop!
-
last_char
-
end
-
6
chunk.sub!(/\.$/, "")
-
-
6
@link_text = chunk
-
6
@uri = ::URI.parse(chunk)
-
6
@process_chunk = process_uri_chunk
-
rescue ::URI::Error => e
-
# warn "rescue parse #{chunk_class}:
-
# '#{m}' #{e.inspect} #{e.backtrace*"\n"}"
-
Rails.logger.warn "rescue parse #{self.class}: #{e.inspect}"
-
end
-
-
1
private
-
-
1
def process_text
-
@link_text
-
end
-
-
1
def process_uri_chunk
-
6
link = format.link_to_resource @link_text, process_text
-
6
"#{link}#{@trailing_punctuation}"
-
end
-
end
-
-
# FIXME: DRY, merge these two into one class
-
1
class EmailURI < URI
-
1
PREPEND_STR = "mailto:".freeze
-
1
EMAIL = '[a-zA-Z\\d](?:[-a-zA-Z\\d.]*[a-zA-Z\\d])?\\@'.freeze
-
-
1
Card::Content::Chunk.register_class(
-
self, prefix_re: "(?:(?!#{REJECTED_PREFIX_RE})#{EMAIL})\\b",
-
full_re: /\A#{::URI.regexp(SCHEMES)}/,
-
prepend_str: PREPEND_STR,
-
idx_char: "@"
-
)
-
-
# removes the prepended string from the unchanged match text
-
1
def process_text
-
6
@text = @text.sub(/^mailto:/, "")
-
end
-
end
-
-
1
class HostURI < URI
-
GENERIC = "aero|biz|com|coop|edu|gov|info|int|mil|" \
-
1
"museum|name|net|org".freeze
-
-
COUNTRY = "ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|az|ba|bb|bd|be|" \
-
"bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cf|cd|cg|" \
-
"ch|ci|ck|cl|cm|cn|co|cr|cs|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|" \
-
"ec|ee|eg|eh|er|es|et|fi|fj|fk|fm|fo|fr|fx|ga|gb|gd|ge|gf|gh|" \
-
"gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|" \
-
"il|in|io|iq|ir|is|it|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|" \
-
"kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|mg|mh|mk|ml|mm|" \
-
"mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|" \
-
"no|np|nr|nt|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|pt|pw|py|" \
-
"qa|re|ro|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|" \
-
"st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tm|tn|to|tp|tr|tt|tv|tw|" \
-
"tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|" \
-
"za|zm|zr|zw|" \
-
1
"eu".freeze # made this separate, since it's not technically
-
# a country -efm
-
# These are needed otherwise HOST will match almost anything
-
-
1
TLDS = "(?:#{GENERIC}|#{COUNTRY})".freeze
-
# TLDS = "(?:#{GENERIC})"
-
-
1
PREPEND_STR = "http://".freeze
-
1
HOST = "(?:[a-zA-Z\\d](?:[-a-zA-Z\\d]*[a-zA-Z\\d])?\\.)+#{TLDS}".freeze
-
-
1
Card::Content::Chunk.register_class(
-
self, prefix_re: "(?:(?!#{REJECTED_PREFIX_RE})#{HOST})\\b",
-
full_re: /\A#{::URI.regexp(SCHEMES)}/,
-
prepend_str: PREPEND_STR
-
)
-
-
# removes the prepended string from the unchanged match text
-
1
def process_text
-
@text = @text.sub(%r{^http://}, "")
-
end
-
end
-
end
-
1
class Card
-
1
class Content
-
1
module Chunk
-
1
class ViewStub < Abstract
-
1
Chunk.register_class(
-
self,
-
prefix_re: Regexp.escape("(StUb"),
-
full_re: /\A\(StUb(.*?)sTuB\)/m,
-
idx_char: "("
-
)
-
-
1
def initialize text, content
-
super
-
end
-
-
1
def interpret match, _content
-
@stub_hash = initial_stub_hash match[1]
-
interpret_hash_values
-
end
-
-
1
def initial_stub_hash string
-
JSON.parse(string).symbolize_keys
-
# MessagePack.unpack(hex_to_bin(string)).symbolize_keys
-
end
-
-
1
def hex_to_bin string
-
string.scan(/../).map { |x| x.hex.chr }.join
-
end
-
-
1
def interpret_hash_values
-
@stub_hash.keys.each do |key|
-
send "interpret_#{key}"
-
end
-
end
-
-
1
def interpret_cast
-
@stub_hash[:cast].symbolize_keys!
-
end
-
-
1
def interpret_view_opts
-
@stub_hash[:view_opts].symbolize_keys!
-
end
-
-
1
def interpret_format_opts
-
hash = @stub_hash[:format_opts]
-
hash.symbolize_keys!
-
hash[:nest_mode] = hash[:nest_mode].to_sym
-
hash[:override] = hash[:override] == "true"
-
hash[:context_names].map!(&:to_name)
-
end
-
-
1
def process_chunk
-
@processed = format.stub_nest @stub_hash
-
end
-
-
1
def result
-
@processed
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Format
-
1
class DataFormat < Format
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
# # require "card/content/diff"
-
-
1
class Card
-
1
class Format
-
# Main Format class for formatting card views in HTML
-
1
class HtmlFormat < Format
-
1
register :html
-
-
1
attr_accessor :options_need_save, :start_time, :skip_autosave
-
-
1
def main?
-
24156
!@main.nil?
-
end
-
-
# is the current card the requested card?
-
1
def focal?
-
595
@focal ||= show_layout? ? main? : depth.zero?
-
end
-
-
1
def default_nest_view
-
# FIXME: not sure this makes sense as a rule...
-
143
card.rule(:default_html_view) || :titled
-
end
-
-
1
def default_item_view
-
56
:bar
-
end
-
-
1
def escape_literal literal
-
8
"<span>#{literal}</span>"
-
end
-
-
1
def mime_type
-
274
"text/html"
-
end
-
-
1
def final_render_call method
-
21498
rendered = super
-
21498
rendered.is_a?(Array) ? output(rendered) : rendered
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
1
class Format
-
1
class TextFormat < Format
-
1
register :text
-
1
register :txt
-
1
aliases["txt"] = "text"
-
-
1
def self.view_caching?
-
# probably overkill. problem was with email text message
-
false
-
end
-
end
-
end
-
end
-
1
class Card
-
# retrieve card from cache or database, or (where needed) instantiate new card
-
1
class Fetch
-
1
include Retrieve
-
1
include Results
-
1
include Store
-
-
1
attr_reader :card, :mark, :opts
-
-
# see arg options in all/fetch
-
1
def initialize *args
-
219438
normalize_args args
-
219438
absolutize_mark
-
219438
validate_opts!
-
end
-
-
1
def retrieve_or_new
-
144896
retrieve_existing
-
144896
update_cache
-
144896
results
-
end
-
-
1
def local_only?
-
9220
opts[:local_only]
-
end
-
-
1
def skip_modules?
-
213243
opts[:skip_modules]
-
end
-
-
1
def normalize_args args
-
219438
@opts = args.last.is_a?(Hash) ? args.pop : {}
-
219438
@mark = Card.id_or_name args
-
end
-
-
1
def absolutize_mark
-
219438
return unless mark.name? && (supercard = opts.dig(:new, :supercard))
-
6320
@mark = mark.absolute_name supercard.name
-
end
-
-
1
def validate_opts!
-
219438
return unless opts[:new] && opts[:skip_virtual]
-
raise Card::Error, "fetch called with new args and skip_virtual"
-
end
-
-
1
def look_in_trash?
-
15927
@opts[:look_in_trash]
-
end
-
-
1
def skip_type_lookup?
-
6502
opts[:skip_virtual] || opts[:skip_type_lookup] # || opts[:new]
-
end
-
end
-
end
-
1
class Card
-
1
class Fetch
-
# polishing fetch results
-
1
module Results
-
1
def results
-
144896
return if card.nil?
-
111127
card.new_card? ? new_result_card : finalize_result_card
-
end
-
-
1
def finalize_result_card
-
101342
card.include_set_modules unless skip_modules?
-
101342
card
-
end
-
-
1
def new_result_card
-
13433
with_new_card do
-
3648
finalize_result_card
-
# must include_set_modules before checking `card.known?`,
-
# in case, eg, set modules override #virtual?
-
3648
card if new_opts || card.known?
-
end
-
end
-
-
1
def with_new_card
-
13433
if new_opts
-
2586
quick_renew || renew
-
10847
elsif opts[:skip_virtual]
-
9785
return nil
-
else
-
1062
assign_name_from_mark
-
end
-
3648
yield
-
end
-
-
1
def renew
-
102
return if new_opts.blank?
-
# Rails.logger.info "renewing: #{mark}, #{new_opts}"
-
102
@card = card.dup
-
102
@card.newish newish_opts
-
end
-
-
1
def newish_opts
-
102
hash = new_opts.clone.reverse_merge name: mark
-
102
if (content = assignable_content(hash.delete(:default_content)))
-
hash[:content] = content
-
end
-
102
hash[:type_lookup] = :force if @force_type_lookup
-
102
hash
-
end
-
-
1
def quick_renew
-
2586
return false unless quick_renew?
-
-
# Rails.logger.info "QUICK renewing: #{mark}, #{new_opts}"
-
2484
update_supercard
-
2484
assign_name_from_mark
-
2484
true
-
end
-
-
1
def update_supercard
-
2484
return unless (sc = new_opts[:supercard])
-
595
@card.supercard = sc
-
595
@card.update_superleft @card.name
-
end
-
-
1
def quick_renew?
-
2586
return true if new_opts.blank?
-
1036
return false if type_change? || name_change?
-
934
return false if fancy_renew?
-
934
quick_content
-
934
true
-
end
-
-
# contains subcards, etc, that quick_renew can't handle
-
1
def fancy_renew?
-
934
test_opts = new_opts.slice :supercard, :name, :type_id, :type, :type_code,
-
:content, :default_content
-
934
new_opts.keys.size > test_opts.keys.size
-
end
-
-
1
def assignable_content default_content
-
1036
new_opts[:content] || (@card.db_content.blank? && default_content)
-
end
-
-
1
def quick_content
-
934
return unless (content = assignable_content(new_opts[:default_content]))
-
@card.content = content
-
end
-
-
1
def name_change?
-
944
return false unless (new_name = new_opts[:name]&.to_name)
-
255
return false if new_name.relative? && mark.name? && mark.absolute?
-
199
new_name.to_s != @card.name.to_s
-
end
-
-
1
def type_change?
-
1036
return true if @card.type_id.nil?
-
1036
type_id = type_id_from_new_opts
-
1036
return true if !type_id && supercard_might_change_type?
-
995
type_id && (type_id != @card.type_id)
-
end
-
-
1
def type_id_from_new_opts
-
1036
type_id = new_opts[:type_id] || new_opts[:type] || new_opts[:type_code]&.to_sym
-
1036
type_id.is_a?(Symbol) ? Codename.id(type_id) : type_id
-
end
-
-
1
def supercard_might_change_type?
-
# ...via type_plus_right rule
-
299
sc = new_opts[:supercard]
-
299
@force_type_lookup = sc&.new? && (sc.type_id != sc.default_type_id)
-
end
-
-
1
def new_opts
-
29238
@new_opts ||= opts[:new]
-
end
-
-
1
def assign_name_from_mark
-
3546
return if opts[:local_only]
-
2450
return unless mark&.to_s != card.name
-
21
card.name = mark.to_s
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Fetch
-
# retrieval and instantiation methods for Card::Fetch
-
1
module Retrieve
-
# look for card in cache. if that doesn't work, look in database
-
1
def retrieve_existing
-
144896
return unless mark.present?
-
111116
retrieve_from_cache || retrieve_from_db
-
end
-
-
1
def retrieve_from_cache
-
111116
@card = Card.send "retrieve_from_cache_by_#{mark_type}",
-
mark_value, @opts[:local_only]
-
111116
@card = nil if card&.new? && look_in_trash?
-
# don't return cached cards if looking in trash -
-
# we want the db version
-
111116
card
-
end
-
-
1
def retrieve_from_db
-
8347
query = retrieval_from_db_query
-
8347
@card = query ? Card.where(query).take : nil
-
8347
@cache_ready = true if card.present? && !card.trash
-
8347
card
-
end
-
-
1
def retrieval_from_db_query
-
8347
return unless (query = retrieval_from_db_query_base)
-
5454
query[:trash] = false unless look_in_trash?
-
5454
query
-
end
-
-
1
def retrieval_from_db_query_base
-
8347
if mark_type == :key && mark.simple?
-
1659
{ key: mark_value }
-
6688
elsif (id = id_from_mark)
-
3795
{ id: id }
-
end
-
end
-
-
1
def id_from_mark
-
6688
mark_type == :id ? mark_value : Lexicon.id(mark_value)
-
end
-
-
# In both the cache and the db, ids and keys are used to retrieve card data.
-
# These methods identify the kind of mark to use and its value
-
1
def mark_type
-
126151
@mark_type ||= mark.is_a?(Integer) ? :id : :key
-
end
-
-
1
def mark_value
-
119463
@mark_value ||= mark.is_a?(Integer) ? mark : mark.key
-
end
-
end
-
end
-
end
-
1
class Card
-
1
class Fetch
-
# lazy cache updates based on results
-
1
module Store
-
1
def update_cache
-
144896
return unless update_cache?
-
-
9220
prep_for_cache
-
9220
Card.write_to_cache card, local_only?
-
end
-
-
1
def update_cache?
-
144896
(cache_ready? || new_for_cache || needs_prep?) && !card&.uncacheable?
-
end
-
-
1
def cache_ready?
-
144896
@cache_ready
-
end
-
-
# instantiate a card as a cache placeholder
-
1
def new_for_cache
-
139540
return unless new_for_cache?
-
3090
args = { name: mark, skip_modules: true }
-
3090
args[:type_lookup] = :skip if skip_type_lookup?
-
3090
args.merge! new_opts.slice(:type, :type_id, :type_code) if eager_caching?
-
3090
@card = Card.new args
-
end
-
-
1
def eager_caching?
-
3090
opts[:eager_cache] && mark.name? && mark.absolute? && new_opts.present?
-
end
-
-
1
def new_for_cache?
-
139540
reusable? && new_card_needed?
-
end
-
-
1
def needs_prep?
-
136450
return unless card.present?
-
-
102681
!(skip_modules? || card.patterns?)
-
end
-
-
1
def new_card_needed?
-
78466
!(card.present? && (card.type_id.present? || skip_type_lookup?))
-
end
-
-
1
def reusable?
-
139540
!(mark.is_a?(Integer) || (mark.blank? && !opts[:new]))
-
end
-
-
# Because Card works by including set-specific ruby modules on singleton classes and
-
# singleton classes generally can't be cached, we can never cache the cards in a
-
# completely ready-to-roll form.
-
#
-
# However, we can optimize considerably by saving the list of ruby modules in
-
# environments where they won't be changing (eg production) or at least the list of
-
# matching set patterns
-
1
def prep_for_cache
-
# return # DELETE ME
-
9220
return if skip_modules?
-
-
3975
Cardio.config.cache_set_module_list ? card.set_modules : card.patterns
-
end
-
end
-
end
-
end
-
1
class Card
-
# Optimized handling of card "rules" (Set+Setting) and preferences.
-
1
module Rule
-
1
class << self
-
1
def global_setting name
-
1675
Auth.as_bot do
-
1675
(card = Card[name]) && !card.db_content.strip.empty? && card.db_content
-
end
-
end
-
-
1
def toggle val
-
3
val.to_s.strip == "1"
-
end
-
-
1
def rule_cache
-
18582
Cache.read
-
end
-
-
1
def preference_cache
-
365
PreferenceCache.read
-
end
-
-
1
def read_rule_cache
-
573
ReadRuleCache.read
-
end
-
-
1
def clear_rule_cache
-
39
Cache.clear
-
end
-
-
1
def clear_preference_cache
-
43
PreferenceCache.clear
-
end
-
-
1
def clear_read_rule_cache
-
ReadRuleCache.clear
-
end
-
-
1
def preference_names user_name, setting_code
-
4
Card.search({ right: { codename: setting_code },
-
left: { left: { type_id: SetID },
-
right: user_name },
-
return: :name },
-
"preference cards for user: #{user_name}")
-
end
-
-
1
def all_user_ids_with_rule_for set_card, setting_code
-
954
cache_key = "#{set_card.rule_cache_key_base}+#{setting_code}"
-
954
user_ids = PreferenceCache.user_ids[cache_key] || []
-
954
user_ids.include?(AllID) ? all_user_ids : user_ids
-
end
-
-
1
private
-
-
1
def all_user_ids
-
Card.where(type_id: UserID).pluck :id
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Rule
-
1
class Cache
-
1
class_attribute :sql, :cache_key
-
-
1
self.sql = %(
-
SELECT
-
rules.id AS rule_id,
-
settings.id AS setting_id,
-
sets.id AS set_id,
-
sets.left_id AS anchor_id,
-
sets.right_id AS set_tag_id
-
FROM cards rules
-
JOIN cards sets ON rules.left_id = sets.id
-
JOIN cards settings ON rules.right_id = settings.id
-
WHERE sets.type_id = #{SetID}
-
AND settings.type_id = #{SettingID}
-
AND (settings.codename != 'follow' OR rules.db_content != '')
-
AND rules.trash is false
-
AND sets.trash is false
-
AND settings.trash is false;
-
).freeze
-
-
1
self.cache_key = "RULES".freeze
-
-
1
class << self
-
1
def read
-
19520
Card.cache.read(cache_key) || populate
-
end
-
-
1
def populate
-
149
Card.cache.write cache_key, lookup_hash
-
end
-
-
1
def clear
-
82
Card.cache.write cache_key, nil
-
end
-
-
1
def lookup_hash
-
131
rows.each_with_object({}) do |row, hash|
-
18817
next unless (key = lookup_key row)
-
18817
hash[key] = row["rule_id"].to_i
-
end
-
end
-
-
1
def lookup_key row
-
19660
return false unless (setting_code = setting_code(row))
-
19660
anchor_id = row["anchor_id"]
-
19660
return false unless (pattern_code = pattern_code(anchor_id, row))
-
-
19660
[anchor_id, pattern_code, setting_code].compact.map(&:to_s).join "+"
-
end
-
-
1
def pattern_code anchor_id, row
-
19660
set_class_id = anchor_id.nil? ? row["set_id"] : row["set_tag_id"]
-
19660
Card::Codename[set_class_id.to_i]
-
end
-
-
1
def setting_code row
-
19660
Card::Codename[row["setting_id"].to_i]
-
end
-
-
1
def rows
-
149
Card.connection.select_all sql
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Rule
-
1
class PreferenceCache < Cache
-
1
self.sql = %(
-
SELECT
-
preferences.id AS rule_id,
-
settings.id AS setting_id,
-
sets.id AS set_id,
-
sets.left_id AS anchor_id,
-
sets.right_id AS set_tag_id,
-
users.id AS user_id
-
FROM cards preferences
-
JOIN cards user_sets ON preferences.left_id = user_sets.id
-
JOIN cards settings ON preferences.right_id = settings.id
-
JOIN cards users ON user_sets.right_id = users.id
-
JOIN cards sets ON user_sets.left_id = sets.id
-
WHERE sets.type_id = #{SetID}
-
AND settings.type_id = #{SettingID}
-
AND (users.type_id = #{UserID} or users.codename = 'all')
-
AND sets.trash is false
-
AND settings.trash is false
-
AND users.trash is false
-
AND user_sets.trash is false
-
AND preferences.trash is false;
-
).freeze
-
-
1
self.cache_key = "PREFERENCES".freeze
-
1
USER_ID_CACHE_KEY = "USER_IDS".freeze
-
-
1
class << self
-
1
def user_ids
-
1008
Card.cache.read(USER_ID_CACHE_KEY) || (populate && user_ids)
-
end
-
-
1
def populate
-
54
@rows = nil
-
54
super.tap do
-
54
populate_user_ids
-
54
@rows = nil
-
end
-
end
-
-
1
def populate_user_ids
-
54
Card.cache.write USER_ID_CACHE_KEY, user_id_hash
-
end
-
-
1
def user_id_hash
-
54
rows.each_with_object({}) do |row, hash|
-
843
key = lookup_key_without_user row
-
843
hash[key] ||= []
-
843
hash[key] << row["user_id"]
-
end
-
end
-
-
1
def clear
-
43
super
-
43
Card.cache.write USER_ID_CACHE_KEY, nil
-
end
-
-
1
def rows
-
108
@rows ||= super
-
end
-
-
1
alias :lookup_key_without_user :lookup_key
-
-
1
def lookup_key row
-
843
return unless (base = lookup_key_without_user row)
-
-
843
"#{base}+#{row['user_id']}"
-
end
-
end
-
end
-
end
-
end
-
1
class Card
-
1
module Rule
-
1
class ReadRuleCache < Cache
-
1
self.sql = %(
-
SELECT
-
refs.referee_id AS party_id,
-
read_rules.id AS read_rule_id
-
FROM cards read_rules
-
JOIN card_references refs ON refs.referer_id = read_rules.id
-
JOIN cards sets ON read_rules.left_id = sets.id
-
WHERE read_rules.right_id = #{ReadID}
-
AND sets.type_id = #{SetID}
-
AND read_rules.trash is false
-
AND sets.trash is false;
-
).freeze
-
-
1
self.cache_key = "READRULES".freeze
-
-
1
class << self
-
1
def lookup_hash
-
18
rows.each_with_object({}) do |row, h|
-
504
party_id = row["party_id"].to_i
-
504
h[party_id] ||= []
-
504
h[party_id] << row["read_rule_id"].to_i
-
end
-
end
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
class Card
-
# Used to extend setting modules like Card::Set::Self::Create in the
-
# settings mod
-
1
module Setting
-
# Let M = Card::Setting (module)
-
# E = Card::Set::Self::Create (module extended with M)
-
# O = Card['*create'] (object)
-
# accessible in E
-
1
attr_accessor :codename
-
# accessible in E and M
-
1
mattr_accessor :groups, :group_names, :user_specific
-
1
def self.extended host_class
-
# accessible in E and O
-
26
host_class.mattr_accessor :restricted_to_type, :rule_type_editable, :short_help_text,
-
:raw_help_text, :right_set, :applies
-
26
setting_class_name = host_class.to_s.split("::").last
-
63
host_class.ensure_set { "Card::Set::Right::#{setting_class_name}" }
-
26
host_class.right_set = Card::Set::Right.const_get(setting_class_name)
-
26
host_class.right_set.mattr_accessor :raw_help_text
-
end
-
-
1
def self.codenames
-
Card::Setting.groups.values.flatten.compact.map(&:codename)
-
end
-
-
@@group_names = {
-
1
templating: "Templating",
-
permission: "Permissions",
-
webpage: "Webpage",
-
editing: "Editing",
-
event: "Events",
-
other: "Other",
-
config: "Config"
-
}
-
1
@@groups = @@group_names.keys.each_with_object({}) do |key, groups|
-
7
groups[key] = []
-
end
-
1
@@user_specific = ::Set.new
-
-
1
def self.user_specific? codename
-
@@user_specific.include? codename
-
end
-
-
# usage:
-
# setting_opts group: :permission | :event | ...
-
# position: <Fixnum> (starting at 1, default: add to end)
-
# rule_type_editable: true | false (default: false)
-
# restricted_to_type: <cardtype> | [ <cardtype>, ...]
-
1
def setting_opts opts
-
26
group = opts[:group] || :other
-
26
@@groups[group] ||= []
-
26
set_position group, opts[:position]
-
-
26
@codename = opts[:codename] ||
-
name.match(/::(\w+)$/)[1].underscore.to_sym
-
26
self.rule_type_editable = opts[:rule_type_editable]
-
26
self.restricted_to_type = permitted_type_ids opts[:restricted_to_type]
-
26
self.short_help_text = opts[:short_help_text]
-
26
self.applies = opts[:applies]
-
26
right_set.raw_help_text = self.raw_help_text = opts[:help_text]
-
26
return unless opts[:user_specific]
-
1
@@user_specific << @codename
-
end
-
-
1
def set_position group, pos
-
26
if pos
-
26
if @@groups[group][pos - 1]
-
2
@@groups[group].insert(pos - 1, self)
-
else
-
24
@@groups[group][pos - 1] = self
-
end
-
else
-
@@groups[group] << self
-
end
-
end
-
-
1
def applies_to_cardtype type_id, prototype=nil
-
(!restricted_to_type || restricted_to_type.include?(type_id)) &&
-
(!prototype || applies_to_prototype?(prototype))
-
end
-
-
1
def applies_to_prototype? prototype
-
return true unless applies
-
applies.call(prototype)
-
end
-
-
1
private
-
-
1
def permitted_type_ids types
-
26
return unless types
-
2
type_ids = Array.wrap(types).flatten.map do |cardtype|
-
6
Card::Codename.id cardtype
-
end
-
2
::Set.new(type_ids)
-
end
-
end
-
end
-
1
class Card
-
1
class Layout
-
1
class << self
-
1
def render layout, format
-
layout_class(layout).new(layout, format).render
-
end
-
-
1
def layout_class layout
-
2
if layout.respond_to? :call
-
Card::Layout::ProcLayout
-
2
elsif card_layout? layout
-
2
Card::Layout::CardLayout
-
elsif code_layout? layout
-
Card::Layout::CodeLayout
-
else
-
Card::Layout::UnknownLayout
-
end
-
end
-
-
1
def card_layout? name
-
226
Card.fetch_type_id(name).in? [Card::LayoutTypeID, Card::HtmlID, Card::BasicID]
-
rescue ArgumentError, Card::Error::CodenameNotFound => _e
-
false
-
end
-
-
1
def code_layout? name
-
built_in_layouts_hash.key? name.to_sym
-
end
-
-
1
def register_layout new_layout
-
6
key = layout_key new_layout
-
6
return if layouts[key]
-
-
6
layouts[key] = block_given? ? yield : {}
-
end
-
-
1
def deregister_layout layout_name
-
layouts.delete layout_key(layout_name)
-
end
-
-
1
def layout_key name
-
230
return name if name.is_a? Symbol
-
226
name.to_name.key.to_sym
-
end
-
-
1
def register_built_in_layout new_layout, opts
-
8
register_layout(new_layout) { opts.present? ? opts : nil }
-
4
built_in_layouts_hash[new_layout] = true
-
end
-
-
1
def built_in_layouts_hash
-
4
@built_in_layouts ||= {}
-
end
-
-
1
def built_in_layouts
-
built_in_layouts_hash.keys
-
end
-
-
1
def layouts
-
236
@layouts ||= {}
-
end
-
-
1
def clear_cache
-
@built_in_layouts = @layouts = nil
-
end
-
-
1
def main_nest_opts layout_name, format
-
224
key = layout_key layout_name
-
224
opts = layouts[key] || register_layout_with_nest(layout_name, format)
-
224
opts.clone
-
end
-
-
1
def register_layout_with_nest layout_name, format
-
2
register_layout(layout_name) do
-
2
layout_class(layout_name).new(layout_name, format).fetch_main_nest_opts
-
end
-
end
-
end
-
-
1
def initialize layout, format
-
226
@layout = layout
-
226
@format = format
-
end
-
-
1
def fetch_main_nest_opts
-
{}
-
end
-
-
1
def main_nest_opts
-
self.class.main_nest_opts @layout, @format
-
end
-
end
-
end
-
1
class Card
-
1
class Layout
-
# Layout based on a card's content
-
1
class CardLayout < Layout
-
1
def layout_card
-
226
@layout_card ||= Card.quick_fetch @layout
-
end
-
-
1
def render
-
224
@format.process_content layout_card.content, chunk_list: :references
-
end
-
-
1
def fetch_main_nest_opts
-
2
main_nest = find_main_nest_chunk
-
# don't .& me !! (can be false)
-
2
(main_nest && main_nest.options) ||
-
raise(Card::Error, "no main nest found in layout \"#{@layout}\"")
-
end
-
-
1
MAIN_NESTING_LIMIT = 5
-
-
1
def find_main_nest_chunk card=layout_card, depth=0
-
2
content = Card::Content.new(card.content, @format, chunk_list: :nest_only)
-
2
return false unless content.each_chunk.count.positive?
-
-
2
main_chunk(content) || go_deeper(content, depth)
-
end
-
-
1
def go_deeper content, depth
-
return false if depth > MAIN_NESTING_LIMIT
-
-
content.each_chunk do |chunk|
-
main_chunk = find_main_nest_chunk chunk.referee_card, depth + 1
-
return main_chunk if main_chunk
-
end
-
false
-
end
-
-
1
def main_chunk content
-
2
content.each_chunk.find(&:main?)
-
end
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (BsBadge)
-
#
-
1
module BsBadge;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/utility/set/abstract/bs_badge.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def labeled_badge count, label, opts={}
-
haml :labeled_badge, badge_haml_opts(count, label, opts)
-
end
-
-
1
def tab_badge count, label, opts={}
-
haml :tab_badge, badge_haml_opts(count, label, opts)
-
end
-
-
1
def badge_haml_opts count, label, opts
-
process_badge_opts count, opts
-
{ count: count, label: label, klass: opts[:klass], color: opts[:color],
-
title: opts[:title] }
-
end
-
-
1
def process_badge_opts count, opts
-
if count.try(:zero?) && !opts[:zero_ok]
-
opts[:klass] = [opts[:klass], "disabled-o"].compact.join " "
-
end
-
opts[:color] ||= "secondary"
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/utility/set/abstract/bs_badge.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (Filterable)
-
#
-
1
module Filterable;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/utility/set/abstract/filterable.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def filterable filter_hash={}, html_opts={}
-
add_class html_opts, "_filterable _noFilterUrlUpdates"
-
html_opts[:data] ||= {}
-
html_opts[:data][:filter] = filter_hash
-
wrap_with :div, yield, html_opts
-
end
-
-
1
def filtering selector=nil
-
selector ||= "._filter-widget:visible"
-
wrap_with :div, yield, class: "_filtering", "data-filter-selector": selector
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/utility/set/abstract/filterable.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (FilterableBar)
-
#
-
1
module FilterableBar;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/utility/set/abstract/filterable_bar.rb"; end
-
1
include_set Abstract::Filterable
-
-
1
before :bar do
-
class_up "bar-body", "_filterable"
-
super()
-
end
-
-
1
before :expanded_bar do
-
class_up "bar", "_filterable"
-
super()
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/utility/set/abstract/filterable_bar.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (Utility)
-
#
-
1
module Utility;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/utility/set/abstract/utility.rb"; end
-
-
1
def fetch_params params
-
Env.params.select { |key, val| val && params.include?(key) }
-
.with_indifferent_access
-
end
-
-
1
def param_to_i key, default
-
if (value = Env.params[key])
-
value.to_i
-
else
-
default
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/utility/set/abstract/utility.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Admin"
-
#
-
# collect arrays of the form
-
1
module Admin;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/admin/set/self/admin.rb"; end
-
# [task symbol, { execute_policy: block, stats_policy: block }]
-
1
basket :tasks
-
-
1
def run_task_from_task_basket task
-
task = task.to_sym
-
task_data = tasks.find {|h| h[:name].to_sym == task.to_sym}
-
if !irreversibles_tasks_allowed? && task_data[:irreversible]
-
not_allowed task_data[:stats][:link_text]
-
else
-
task_data[:execute_policy].call if task_data
-
end
-
end
-
-
1
event :admin_tasks, :initialize, on: :update do
-
return unless (task = Env.params[:task])
-
raise Card::Error::PermissionDenied, self unless Auth.always_ok?
-
case task.to_sym
-
when :clear_cache then Card::Cache.reset_all
-
when :repair_references then Card::Reference.repair_all
-
when :repair_permissions then Card.repair_all_permissions
-
when :clear_solid_cache then Card.clear_solid_cache
-
when :clear_machine_cache then Card.reset_all_machines
-
when :clear_script_cache then Card.reset_script_machine
-
when :clear_history
-
not_allowed "clear history" unless irreversibles_tasks_allowed?
-
Card::Action.delete_old
-
else
-
run_task_from_task_basket task
-
end
-
abort :success
-
end
-
-
1
def not_allowed task
-
raise Card::Error::PermissionDenied,
-
"The admin task '#{task}' is disabled for security reasons.<br>"\
-
"You can enable it with the config option 'allow_irreversible_admin_tasks'"
-
end
-
-
1
def irreversibles_tasks_allowed?
-
Cardio.config.allow_irreversible_admin_tasks
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :core, cache: :never do
-
stats = card_stats
-
stats += cache_stats
-
stats += memory_stats
-
card.tasks.each do |task|
-
stats += Array.wrap task[:stats]
-
end
-
table_content = stats.map { |args| stat_row(args) }
-
table table_content, header: %w[Stat Value Action]
-
end
-
-
1
def card_stats
-
[
-
{ title: "cards",
-
count: Card.where(trash: false) },
-
{ title: "actions",
-
count: Card::Action },
-
# link_text: "clear history",
-
# task: "clear_history" },
-
{ title: "references",
-
count: Card::Reference }
-
# link_text: "repair all",
-
# task: "repair_references" }
-
]
-
end
-
-
1
def cache_stats
-
[
-
{ title: "solid cache",
-
count: solid_cache_count, unit: " cards",
-
link_text: "clear solid cache",
-
task: "clear_solid_cache" },
-
{ title: "machine cache",
-
count: machine_cache_count, unit: " cards",
-
link_text: "clear machine cache",
-
task: "clear_machine_cache" }
-
]
-
# return stats unless Card.config.view_cache#
-
# stats << { title: "view cache",
-
# count: Card::View,
-
# link_text: "clear view cache",
-
# task: "clear_view_cache" }
-
end
-
-
1
def memory_stats
-
oldmem = session[:memory]
-
session[:memory] = newmem = card.profile_memory
-
stats = [
-
{ title: "memory now",
-
count: newmem, unit: "M",
-
link_text: "clear cache", task: "clear_cache" }
-
]
-
return stats unless oldmem
-
stats << { title: "memory prev", count: oldmem, unit: "M" }
-
stats << { title: "memory diff", count: newmem - oldmem, unit: "M" }
-
stats
-
end
-
-
1
def stat_row args={}
-
res = [(args[:title] || "")]
-
res << "#{count(args[:count])}#{args[:unit]}"
-
return res unless args[:task]
-
res << link_to_card(:admin, (args[:link_text] || args[:task]),
-
path: { action: :update, task: args[:task] })
-
res
-
end
-
-
1
def count counter
-
counter = counter.call if counter.is_a?(Proc)
-
counter.respond_to?(:count) ? counter.count : counter
-
end
-
-
1
def solid_cache_count
-
Card.search right: { codename: "solid_cache" }, return: "count"
-
end
-
-
1
def machine_cache_count
-
Card::Virtual.where(right_id: MachineCacheID).count
-
end
-
-
1
def delete_sessions_link months
-
link_to_card :admin, months, path: { action: :update, months: months,
-
task: "delete_old_sessions" }
-
end
-
end
-
-
1
def current_memory_usage
-
`ps -o rss= -p #{Process.pid}`.to_i
-
end
-
-
1
def profile_memory &block
-
before = current_memory_usage
-
if block_given?
-
instance_eval(&block)
-
else
-
before = 0
-
end
-
(current_memory_usage - before) / 1024.to_i
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/admin/set/self/admin.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "AdminInfo"
-
#
-
1
module AdminInfo;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/admin/set/self/admin_info.rb"; end
-
1
basket :warnings
-
-
# For each warning in the basket (eg :my_warning), the core view
-
# will run a test by appending a question mark (eg #my_warning?).
-
# If it fails it will generate a message by appending message
-
# (eg #my_warning_message).
-
-
1
add_to_basket :warnings, :no_email_delivery
-
-
1
def no_email_delivery?
-
Card.config.action_mailer.perform_deliveries == false
-
end
-
-
1
def clean_html?
-
false
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
warnings = card.warnings.map do |warning|
-
card.send("#{warning}?") ? send("#{warning}_message") : nil
-
end
-
warnings.compact!
-
warnings.empty? ? "" : warning_alert(warnings)
-
end
-
-
1
def warning_alert warnings
-
admin_warn = I18n.t(:admin_warn, scope: "mod.admin.set.self.admin_info")
-
# 'ADMINISTRATOR WARNING'
-
alert :warning, true do
-
"<h5>#{admin_warn}</h5>" + list_tag(warnings)
-
end
-
end
-
-
1
def no_email_delivery_message
-
# "Email delivery is turned off."
-
# "Change settings in config/application.rb to send sign up notifications."
-
I18n.t(:email_off,
-
scope: "mod.admin.set.self.admin_info",
-
path: "config/application.rb")
-
end
-
-
1
def warning_list_with_auto_scope warnings
-
# 'ADMINISTRATOR WARNING'
-
admin_warn = tr(:admin_warn)
-
"<h5>#{admin_warn}</h5>" + warnings.join("\n")
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/admin/set/self/admin_info.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Debugger"
-
#
-
1
module Debugger;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/admin/set/self/debugger.rb"; end
-
1
def raw_help_text
-
"show more useful error pages"
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/admin/set/self/debugger.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Trash"
-
#
-
1
module Trash;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/admin/set/self/trash.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
rows = trashed_cards.map { |tc| trash_table_row(tc) }
-
output [
-
restored,
-
(empty_trash_link if rows.present?),
-
table(rows, header: ["card", "deleted", "by", ""])
-
]
-
end
-
-
1
def trashed_cards
-
Card.where(trash: true).order(updated_at: :desc)
-
end
-
-
1
def trash_table_row card
-
[
-
card.name,
-
"#{time_ago_in_words(card.updated_at)} ago",
-
Card[card.updater_id].name,
-
"#{history_link(card)} | #{restore_link(card)}"
-
]
-
end
-
-
1
def restored
-
return unless (res_id = Env.params[:restore]) &&
-
(res_card = Card[res_id.to_i])
-
alert :success, true do
-
wrap_with(:h5, "restored") + subformat(res_card).render_bar
-
end
-
end
-
-
1
def empty_trash_link
-
wrap_with(
-
:p,
-
button_link("empty trash",
-
btn_type: :default,
-
path: { mark: :admin, action: :update, task: :empty_trash,
-
success: { id: "~#{card.id}" } },
-
"data-confirm" => "Are you sure you want to delete "\
-
"all cards in the trash?")
-
)
-
end
-
-
1
def history_link trashed_card
-
link_to_card trashed_card, "history",
-
path: { view: :history, look_in_trash: true }
-
end
-
-
1
def restore_link trashed_card
-
before_delete = trashed_card.actions[-2]
-
link_to "restore", method: :post,
-
rel: "nofollow",
-
remote: true,
-
class: "slotter",
-
path: { id: trashed_card.id,
-
view: :open,
-
look_in_trash: true,
-
action: :update,
-
restore: trashed_card.id,
-
action_ids: [before_delete],
-
success: { id: "~#{card.id}" } }
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/admin/set/self/trash.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Version"
-
#
-
# -*- encoding : utf-8 -*-
-
-
1
module Version;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/admin/set/self/version.rb"; end
-
# require "card/version"
-
-
1
def ok_to_read
-
224
true
-
end
-
-
1
def content
-
224
Card::Version.release
-
end
-
-
# view :core, :raw
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/admin/set/self/version.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (CodeFile)
-
#
-
1
module CodeFile;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/abstract/code_file.rb"; end
-
1
def self.included host_class
-
34
host_class.mattr_accessor :file_content_mod_name
-
34
host_class.file_content_mod_name = Card::Set.mod_name(caller)
-
end
-
-
# FIXME: these should abstracted and configured on the types
-
# (same codes for `rake card:create:codefile`)
-
-
# @return [Array<String>, String] the name of file(s) to be loaded
-
1
def source_files
-
case type_id
-
when CoffeeScriptID then "#{codename}.js.coffee"
-
when JavaScriptID then "#{codename}.js"
-
when CssID then "#{codename}.css"
-
when ScssID then "#{codename}.scss"
-
end
-
end
-
-
1
def source_dir
-
case type_id
-
when CoffeeScriptID, JavaScriptID then "lib/javascript"
-
when CssID, ScssID then "lib/stylesheets"
-
else
-
"lib"
-
end
-
end
-
-
1
def find_file filename
-
modname = file_content_mod_name
-
modname = $1 if modname =~ /^card-mod-(\w*)/
-
mod_path = Card::Mod.dirs.path modname
-
file_path = File.join(mod_path, source_dir, filename)
-
unless File.exist?(file_path)
-
Rails.logger.info "couldn't locate file #{filename} at #{file_path}"
-
return nil
-
end
-
file_path
-
end
-
-
1
def existing_source_paths
-
Array.wrap(source_files).map do |filename|
-
find_file(filename)
-
end.compact
-
end
-
-
1
def source_changed? since:
-
existing_source_paths.any? { |path| ::File.mtime(path) > since }
-
end
-
-
1
def content
-
Array.wrap(source_files).map do |filename|
-
if (source_path = find_file filename)
-
Rails.logger.info "reading file: #{source_path}"
-
File.read source_path
-
end
-
end.compact.join "\n"
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :input do
-
"Content is stored in file and can't be edited."
-
end
-
-
1
view :file_size do
-
"#{card.name}: #{number_to_human_size card.content.bytesize}"
-
end
-
-
1
def short_content
-
fa_icon("exclamation-circle", class: "text-muted pr-2") +
-
wrap_with(:span, "file", class: "text-muted")
-
end
-
-
1
def standard_submit_button
-
multi_card_editor? ? super : ""
-
end
-
end
-
-
1
def coffee_files files
-
files.map { |f| "script_#{f}.js.coffee" }
-
end
-
-
1
def scss_files files
-
files.map { |f| "style_#{f}.scss" }
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/abstract/code_file.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (HamlFile)
-
#
-
1
module HamlFile;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/abstract/haml_file.rb"; end
-
-
1
def self.included host_class
-
host_class.mattr_accessor :template_path
-
host_class.extend Card::Set::Format::HamlPaths
-
host_class.template_path = host_class.haml_template_path
-
end
-
-
1
def content
-
File.read template_path
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :input do
-
"Content is managed by code and cannot be edited"
-
end
-
-
1
def haml_locals
-
{}
-
end
-
-
1
view :core do
-
haml card.content, haml_locals
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/abstract/haml_file.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (Lock)
-
#
-
1
module Lock;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/abstract/lock.rb"; end
-
1
def lock
-
was_already_locked = locked?
-
return if was_already_locked
-
Auth.as_bot do
-
lock!
-
yield
-
end
-
ensure
-
unlock! unless was_already_locked
-
end
-
-
1
def lock_cache_key
-
"UPDATE-LOCK:#{key}"
-
end
-
-
1
def locked?
-
Card.cache.read lock_cache_key
-
end
-
-
1
def lock!
-
Card.cache.write lock_cache_key, true
-
end
-
-
1
def unlock!
-
Card.cache.write lock_cache_key, false
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/abstract/lock.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (VendorCodeFile)
-
#
-
1
module VendorCodeFile;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/abstract/vendor_code_file.rb"; end
-
1
include_set Abstract::CodeFile
-
-
1
def self.included host_class
-
1
host_class.mattr_accessor :file_content_mod_name
-
1
host_class.file_content_mod_name = Card::Set.mod_name(caller)
-
end
-
-
1
def source_dir
-
"vendor"
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/abstract/vendor_code_file.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Abort)
-
#
-
1
module Abort;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/abort.rb"; end
-
-
# The Card#abort method is for cleanly exiting an action without continuing
-
# to process any further events.
-
#
-
# Three statuses are supported:
-
#
-
# failure: adds an error, returns false on save
-
# success: no error, returns true on save
-
# triumph: similar to success, but if called on a subcard
-
# it causes the entire action to abort (not just the subcard)
-
1
def abort status, msg="action canceled"
-
88
director.abort
-
88
if status == :failure && errors.empty?
-
errors.add :abort, msg
-
88
elsif status.is_a?(Hash) && status[:success]
-
success << status[:success]
-
status = :success
-
end
-
88
raise Card::Error::Abort.new(status, msg)
-
end
-
-
1
def aborting
-
1
yield
-
1
errors.any? ? abort(:failure) : abort(:success)
-
end
-
-
1
def abortable
-
2187
yield
-
rescue Card::Error::Abort => e
-
88
handle_abort_error e
-
end
-
-
1
private
-
-
1
def handle_abort_error e
-
88
if e.status == :triumph
-
@supercard ? raise(e) : true
-
88
elsif e.status == :success
-
88
abort_success
-
end
-
end
-
-
1
def abort_success
-
88
if @supercard
-
@supercard.subcards.delete key
-
@supercard.director.subdirectors.delete self
-
expire :soft
-
end
-
88
true
-
end
-
-
# this is an override of standard rails behavior that rescues abort
-
# makes it so that :success abortions do not rollback
-
1
def with_transaction_returning_status
-
411
status = nil
-
411
self.class.transaction do
-
411
add_to_transaction
-
411
remember_transaction_record_state
-
822
status = abortable { yield }
-
406
raise ActiveRecord::Rollback unless status
-
end
-
406
status
-
end
-
-
# FIXME: these two do not belong here!
-
-
1
public
-
-
1
event :notable_exception_raised do
-
error = Card::Error.current
-
Rails.logger.debug "#{error.message}\n#{error.backtrace * "\n "}"
-
end
-
-
1
def success
-
18
Env.success(name)
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/abort.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Actify)
-
#
-
1
module Actify;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/actify.rb"; end
-
1
def act options={}, &block
-
905
if act_card
-
411
add_to_act options, &block
-
else
-
494
start_new_act &block
-
end
-
end
-
-
1
def act_card
-
2840
Card::Director.act_card
-
end
-
-
1
def act_card?
-
390
self == act_card
-
end
-
-
1
module ClassMethods
-
1
def create! opts
-
card = Card.new opts
-
card.save!
-
card
-
end
-
-
1
def create opts
-
1
card = Card.new opts
-
1
card.save
-
1
card
-
end
-
end
-
-
1
def save! *args
-
306
as_subcard = args.first&.delete :as_subcard
-
612
act(as_subcard: as_subcard) { super }
-
end
-
-
1
def save(*)
-
2
act { super }
-
end
-
-
1
def valid?(*)
-
358
act(validating: true) { super }
-
end
-
-
1
def update *args
-
act { super }
-
end
-
-
1
def update! *args
-
208
act { super }
-
end
-
-
1
alias_method :update_attributes, :update
-
1
alias_method :update_attributes!, :update!
-
-
1
private
-
-
1
def start_new_act
-
494
self.director = nil
-
494
Director.run_act(self) do
-
988
run_callbacks(:act) { yield }
-
end
-
end
-
-
1
def add_to_act options={}
-
411
director.appoint self unless @director
-
411
director.head = true unless options[:validating] || options[:as_subcard]
-
411
yield
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/actify.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (ActiveCard)
-
#
-
1
module ActiveCard;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/active_card.rb"; end
-
-
# FIXME: -this needs a better home!
-
1
def format opts={}
-
558
opts = { format: opts.to_sym } if [Symbol, String].member? opts.class
-
558
Card::Format.new self, opts
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/active_card.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (AssignAttributes)
-
#
-
1
module AssignAttributes;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/assign_attributes.rb"; end
-
-
1
def assign_attributes args={}
-
4223
args = prepare_assignment_args args
-
-
4223
assign_with_subcards args do
-
4223
assign_with_set_modules args do
-
4223
params = prepare_assignment_params args
-
4223
super params
-
end
-
end
-
end
-
-
1
def assign_set_specific_attributes
-
15651
set_specific.each_pair do |name, value|
-
63
send "#{name}=", value
-
end
-
end
-
-
1
def extract_subcard_args! args
-
4223
subcards = args.delete("subcards") || args.delete(:subcards) || {}
-
4223
if (subfields = args.delete("subfields") || args.delete(:subfields))
-
subfields.each_pair do |key, value|
-
subcards[name.field(key)] = value
-
end
-
end
-
4223
args.keys.each do |key|
-
5128
subcards[key] = args.delete(key) if key =~ /^\+/
-
end
-
4223
subcards = subcards.to_unsafe_h if subcards.respond_to?(:to_unsafe_h)
-
4223
subcards
-
end
-
-
1
protected
-
-
1
module ClassMethods
-
1
def assign_or_newish name, attributes, fetch_opts={}
-
253
if (known_card = Card.fetch(name, fetch_opts))
-
39
known_card.refresh.newish attributes
-
39
known_card
-
else
-
214
Card.new attributes.merge(name: name)
-
end
-
end
-
end
-
-
1
def prepare_assignment_params args
-
4223
args = args.to_unsafe_h if args.respond_to?(:to_unsafe_h)
-
4223
params = ActionController::Parameters.new(args)
-
4223
params.permit!
-
4223
params[:db_content] = standardize_content(params[:db_content]) if params[:db_content]
-
4223
params
-
end
-
-
1
def prepare_assignment_args args
-
4223
return {} unless args
-
4223
args = args.stringify_keys
-
4223
normalize_type_attributes args
-
4223
stash_set_specific_attributes args
-
4223
args
-
end
-
-
1
def assign_with_set_modules args
-
4223
set_changed = args["name"] || args["type_id"]
-
4223
return yield unless set_changed
-
-
8166
refresh_set_modules { yield }
-
end
-
-
1
def assign_with_subcards args
-
4223
subcard_args = extract_subcard_args! args
-
4223
yield
-
# name= must come before process subcards
-
4223
return unless subcard_args.present?
-
104
subcards.add subcard_args
-
end
-
-
1
def refresh_set_modules
-
4083
reinclude_set_modules = @set_mods_loaded
-
4083
yield
-
4083
reset_patterns
-
4083
include_set_modules if reinclude_set_modules
-
end
-
-
1
def stash_set_specific_attributes args
-
4223
@set_specific = {}
-
4223
Card.set_specific_attributes.each do |key|
-
38007
set_specific[key] = args.delete(key) if args.key?(key)
-
end
-
end
-
-
1
def normalize_type_attributes args
-
4223
new_type_id = extract_type_id! args unless args.delete("type_lookup") == :skip
-
4223
args["type_id"] = new_type_id if new_type_id
-
end
-
-
1
def extract_type_id! args={}
-
case
-
2236
when (type_id = args.delete("type_id")&.to_i)
-
199
type_id.zero? ? nil : type_id
-
2037
when (type_code = args.delete("type_code")&.to_sym)
-
type_id_from_codename type_code
-
2037
when (type_name = args.delete "type")
-
579
type_id_from_cardname type_name
-
end
-
end
-
-
1
def type_id_from_codename type_code
-
type_id_or_error(type_code) { Card::Codename.id type_code }
-
end
-
-
1
def type_id_from_cardname type_name
-
1158
type_id_or_error(type_name) { Card.fetch_id type_name }
-
end
-
-
1
def type_id_or_error val
-
579
type_id = yield
-
579
return type_id if type_id
-
-
errors.add :type, "#{val} is not a known type."
-
nil
-
end
-
-
# 'set' refers to the noun not the verb
-
1
def set_specific
-
17079
@set_specific ||= {}
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/assign_attributes.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Cache)
-
#
-
1
module Cache;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/cache.rb"; end
-
1
module ClassMethods
-
1
def retrieve_from_cache cache_key, local_only=false
-
111239
return unless cache
-
111239
local_only ? cache.soft.read(cache_key) : cache.read(cache_key)
-
end
-
-
1
def retrieve_from_cache_by_id id, local_only=false
-
30216
key = Card::Lexicon.name(id)&.key
-
30216
return unless key.present?
-
-
30216
retrieve_from_cache key, local_only if key
-
end
-
-
1
def retrieve_from_cache_by_key key, local_only=false
-
81023
retrieve_from_cache key, local_only
-
end
-
-
1
def write_to_cache card, local_only=false
-
9264
if local_only
-
235
write_to_soft_cache card
-
9029
elsif cache
-
9029
cache.write card.key, card
-
end
-
end
-
-
1
def write_to_soft_cache card
-
710
return unless cache
-
710
cache.soft.write card.key, card
-
end
-
-
1
def expire name
-
43
key = name.to_name.key
-
43
return unless (card = Card.cache.read key)
-
43
card.expire
-
end
-
end
-
-
1
def update_soft_cache
-
Card.write_to_soft_cache self
-
end
-
-
1
def expire_pieces
-
name.piece_names.each do |piece|
-
Card.expire piece
-
end
-
end
-
-
1
def expire cache_type=nil
-
540
return unless (cache_class = cache_class_from_type cache_type)
-
540
expire_views
-
540
expire_names cache_class
-
540
expire_id cache_class
-
end
-
-
1
def cache_class_from_type cache_type
-
540
cache_type ? Card.cache.send(cache_type) : Card.cache
-
end
-
-
1
def view_cache_clean?
-
!db_content_changed?
-
end
-
-
1
def view_cache_keys
-
540
@view_cache_keys ||= hard_read_view_cache_keys || []
-
end
-
-
1
def ensure_view_cache_key cache_key
-
return if view_cache_keys.include? cache_key
-
-
@view_cache_keys << cache_key
-
hard_write_view_cache_keys
-
end
-
-
1
def hard_read_view_cache_keys
-
520
Card.cache.hard&.read_attribute key, :view_cache_keys
-
end
-
-
1
def hard_write_view_cache_keys
-
# puts "WRITE VIEW CACHE KEYS (#{name}): #{view_cache_keys}"
-
Card.cache.hard&.write_attribute key, :view_cache_keys, view_cache_keys
-
end
-
-
1
def expire_views
-
# puts "EXPIRE VIEW CACHE (#{name}): #{view_cache_keys}"
-
540
return unless view_cache_keys.present?
-
Array.wrap(view_cache_keys).each do |view_cache_key|
-
Card::View.cache.delete view_cache_key
-
end
-
@view_cache_keys = []
-
hard_write_view_cache_keys
-
end
-
-
1
def expire_names cache
-
540
[name, name_before_act].uniq.each do |name_version|
-
778
expire_name name_version, cache
-
end
-
end
-
-
1
def expire_name name_version, cache
-
778
return unless name_version.present?
-
545
key_version = name_version.to_name.key
-
545
return unless key_version.present?
-
545
cache.delete key_version
-
end
-
-
1
def expire_id cache
-
540
return unless id.present?
-
389
cache.delete "~#{id}"
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/cache.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Chunk)
-
#
-
1
module Chunk;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/chunk.rb"; end
-
-
1
def chunks content, type, named=false
-
34
content ||= self.content
-
34
type ||= Card::Content::Chunk
-
34
all_chunks = Card::Content.new(content, self).find_chunks type
-
34
named ? all_chunks.select(&:referee_name) : all_chunks
-
end
-
-
1
def reference_chunks content=nil, named=true
-
chunks content, Card::Content::Chunk::Reference, named
-
end
-
-
# named=true rejects commented nests
-
1
def nest_chunks content=nil, named=true
-
34
chunks content, Card::Content::Chunk::Nest, named
-
end
-
-
# named=true rejects external links (since the don't refer to a card name)
-
1
def link_chunks content=nil, named=false
-
chunks content, Card::Content::Chunk::Link, named
-
end
-
-
1
def each_item_name_with_options content=nil
-
reference_chunks(content).each do |chunk|
-
options = chunk.respond_to?(:options) ? chunk.options : {}
-
yield chunk.referee_name, options
-
end
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
def nest_chunks content=nil
-
34
content ||= _render_raw
-
34
card.nest_chunks content
-
end
-
-
1
def nested_cards content=nil
-
nest_chunks(content).map(&:referee_card).uniq
-
end
-
-
1
def edit_fields
-
637
voo.edit_structure || []
-
end
-
-
1
def nested_field_names content=nil
-
15
nest_chunks(content).map(&:referee_name).select { |n| field_name? n }
-
end
-
-
1
def nested_field_cards content=nil
-
nested_cards(content).select { |c| field_name? c.name }
-
end
-
-
1
def field_name? name
-
81
name.field_of? card.name
-
end
-
-
# @return [Array] of Arrays. each is [nest_name, nest_options_hash]
-
1
def edit_field_configs fields_only=false
-
110
if edit_fields.present?
-
82
explicit_edit_fields_config # explicitly configured in voo or code
-
else
-
28
implicit_edit_fields_config fields_only # inferred from nests
-
end
-
end
-
-
1
def implicit_edit_fields_config fields_only
-
28
result = []
-
28
each_nested_chunk(fields: fields_only) do |chunk|
-
51
result << [chunk.options[:nest_name], chunk.options]
-
end
-
28
result
-
end
-
-
1
def each_nested_field_chunk &block
-
each_nested_chunk fields: true, &block
-
end
-
-
1
def each_nested_chunk content: nil, fields: false, uniq: true, virtual: true, &block
-
28
return unless block_given?
-
28
chunks = prepare_nested_chunks content, fields, uniq
-
28
process_nested_chunks chunks, virtual, &block
-
end
-
-
1
def uniq_chunks chunks
-
28
processed = ::Set.new [card.key]
-
28
chunks.select do |chunk|
-
51
key = chunk.referee_name.key
-
51
ok = !processed.include?(key)
-
51
processed << key
-
51
ok
-
end
-
end
-
-
1
def field_chunks chunks
-
100
chunks.select { |chunk| field_name?(chunk.referee_name) }
-
end
-
-
1
private
-
-
1
def prepare_nested_chunks content, fields, uniq
-
28
chunks = nest_chunks content
-
28
chunks = field_chunks chunks if fields
-
28
chunks = uniq_chunks chunks if uniq
-
28
chunks
-
end
-
-
1
def process_nested_chunks chunks, virtual, &block
-
28
chunks.each do |chunk|
-
51
process_nested_chunk chunk, virtual, &block
-
end
-
end
-
-
1
def process_nested_chunk chunk, virtual, &block
-
51
if chunk.referee_card&.virtual?
-
process_nested_virtual_chunk chunk, &block unless virtual
-
else
-
51
yield chunk
-
end
-
end
-
-
1
def process_virtual_chunk chunk
-
subformat(chunk.referee_card).each_nested_field_chunk { |sub_chunk| yield sub_chunk }
-
end
-
-
1
def explicit_edit_fields_config
-
82
edit_fields.map do |cardish, options|
-
163
field_mark = normalized_edit_field_mark cardish, options
-
163
options = normalized_edit_field_options options, Card::Name[field_mark]
-
163
[field_mark, options]
-
end
-
end
-
-
1
def normalized_edit_field_options options, cardname
-
163
options ||= cardname
-
163
options.is_a?(String) ? { title: options } : options
-
end
-
-
1
def normalized_edit_field_mark cardish, options
-
163
return cardish if cardish.is_a?(Card) ||
-
(options.is_a?(Hash) && options.delete(:absolute))
-
163
card.name.field cardish
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/chunk.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Codename)
-
#
-
1
module Codename;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/codename.rb"; end
-
1
def codename
-
4175
super&.to_sym
-
end
-
-
1
event :validate_codename, :validate, on: :update, changed: :codename do
-
validate_codename_permission
-
validate_codename_uniqueness
-
end
-
-
1
event :reset_codename_cache, :integrate, changed: :codename do
-
140
return if action == :create && codename.nil?
-
-
6
Card::Codename.reset_cache
-
6
Card::Codename.generate_id_constants
-
end
-
-
1
private
-
-
1
def validate_codename_permission
-
return if Auth.always_ok? || Auth.as_id == creator_id
-
-
errors.add :codename, tr(:only_admins_codename)
-
end
-
-
1
def validate_codename_uniqueness
-
return (self.codename = nil) if codename.blank?
-
return if errors.present? || !Card.find_by_codename(codename)
-
errors.add :codename, tr(:error_code_in_use, codename: codename)
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/codename.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Collection)
-
#
-
# shared methods for card collections (Pointers, Searches, Sets, etc.)
-
1
module Collection;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/collection.rb"; end
-
1
module ClassMethods
-
1
def search spec, comment=nil
-
681
results = ::Card::Query.run(spec, comment)
-
681
if block_given? && results.is_a?(Array)
-
results.each { |result| yield result }
-
end
-
681
results
-
end
-
-
1
def count_by_cql spec
-
6
spec = spec.clone
-
6
spec.delete(:offset)
-
6
search spec.merge(return: "count")
-
end
-
-
1
def find_each options={}
-
# this is a copy from rails (3.2.16) and is needed because this
-
# is performed by a relation (ActiveRecord::Relation)
-
find_in_batches(options) do |records|
-
records.each { |record| yield record }
-
end
-
end
-
-
1
def find_in_batches options={}
-
if block_given?
-
super(options) do |records|
-
yield(records)
-
Card::Cache.reset_soft
-
end
-
else
-
super(options)
-
end
-
end
-
end
-
-
1
def collection?
-
1
item_cards != [self]
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
view :count do
-
card.item_names.size
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :carousel do
-
bs_carousel unique_id, 0 do
-
nest_item_array.each do |rendered_item|
-
item(rendered_item)
-
end
-
end
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/collection.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Content)
-
#
-
1
module Content;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/content.rb"; end
-
1
def content= value
-
391
self.db_content = standardize_content(value)
-
end
-
-
1
def content
-
9884
structured_content || standard_content
-
end
-
1
alias raw_content content #DEPRECATED!
-
-
1
def content?
-
3
content.present?
-
end
-
-
1
def standard_content
-
9781
db_content || (new_card? && template.db_content)
-
end
-
-
1
def standardize_content value
-
616
value.is_a?(Array) ? value.join("\n") : value
-
end
-
-
1
def structured_content
-
9884
structure && template.db_content
-
end
-
-
1
def refresh_content
-
self.content = Card.find(id)&.db_content
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
ONE_LINE_CHARACTER_LIMIT = 60
-
-
1
def chunk_list # override to customize by set
-
870
:default
-
end
-
-
1
view :one_line_content do
-
with_nest_mode :compact do
-
one_line_content
-
end
-
end
-
-
# DEPRECATED
-
1
view :closed_content, :one_line_content
-
-
1
view :raw_one_line_content do
-
raw_one_line_content
-
end
-
-
1
view :label do
-
42
card.label.to_s
-
end
-
-
1
view :smart_label, cache: :never, unknown: true do
-
42
label_with_description render_label, label_description
-
end
-
-
1
def label_with_description label, description
-
42
return label unless description
-
-
"#{label} #{popover_link description}"
-
end
-
-
# TODO: move this into a nest once popovers are stub safe
-
1
def label_description
-
42
return unless (desc = card.field :description)
-
-
desc.format.render_core
-
end
-
-
1
def raw_one_line_content
-
cut_with_ellipsis render_raw
-
end
-
-
1
def one_line_content
-
Card::Content.smart_truncate render_core
-
end
-
-
1
def cut_with_ellipsis text, limit=one_line_character_limit
-
if text.size <= limit
-
text
-
else
-
text[0..(limit - 3)] + "..."
-
end
-
end
-
-
1
def one_line_character_limit
-
voo.size || ONE_LINE_CHARACTER_LIMIT
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :hidden_content_field, unknown: true, cache: :never do
-
194
hidden_field :content, class: "d0-card-content"
-
end
-
end
-
-
# seems like this should be moved to format so we can fall back on title
-
# rather than name. (In fact, name, title, AND label is a bit much.
-
# Trim to 2?)
-
1
def label
-
42
name
-
end
-
-
1
def creator
-
Card[creator_id]
-
end
-
-
1
def updater
-
13
Card[updater_id]
-
end
-
-
1
def save_content_draft _content
-
clear_drafts
-
end
-
-
1
def clear_drafts
-
210
drafts.created_by(Card::Auth.current_id).each(&:delete)
-
end
-
-
1
def last_draft_content
-
drafts.last.card_changes.last.value
-
end
-
-
1
event :set_content, :store, on: :save do
-
210
self.db_content = prepare_db_content
-
210
@selected_action_id = @selected_content = nil
-
210
clear_drafts
-
end
-
-
1
event :save_draft, :store, on: :update, when: :draft? do
-
save_content_draft content
-
abort :success
-
end
-
-
1
event :set_default_content,
-
:prepare_to_validate,
-
on: :create, when: :use_default_content? do
-
39
self.db_content = template.db_content
-
end
-
-
1
def draft?
-
76
Env.params["draft"] == "true"
-
end
-
-
1
def prepare_db_content
-
210
cont = standard_db_content || "" # necessary?
-
210
clean_html? ? Card::Content.clean!(cont) : cont
-
end
-
-
1
def standard_db_content
-
210
if structure
-
# do not override db_content with content from structure
-
47
db_content
-
else
-
163
standard_content
-
end
-
end
-
-
1
def clean_html?
-
207
true
-
end
-
-
1
def use_default_content?
-
285
!db_content_changed? && template && template.db_content.present?
-
end
-
-
1
def unfilled?
-
181
blank_content? && blank_comment? && !subcards?
-
end
-
-
1
def blank_content?
-
181
content.blank? || content.strip.blank?
-
end
-
-
1
def blank_comment?
-
26
comment.blank? || comment.strip.blank?
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/content.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (ContextualContent)
-
#
-
1
module ContextualContent;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/contextual_content.rb"; end
-
1
def context_card
-
9767
@context_card || self
-
end
-
-
1
def with_context context_card
-
60
old_context = @context_card
-
60
@context_card = context_card if context_card
-
60
yield
-
ensure
-
60
@context_card = old_context
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
def context_card
-
5196
card.context_card
-
end
-
-
1
def with_context context_card
-
60
card.with_context context_card do
-
60
yield
-
end
-
end
-
-
1
def contextual_content context_card, options={}
-
60
view = options.delete(:view) || :core
-
120
with_context(context_card) { render! view, options }
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/contextual_content.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Debug)
-
#
-
1
module Debug;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/debug.rb"; end
-
1
def to_s
-
21
"#<#{self.class.name}[#{debug_type}]#{attributes['name']}>"
-
end
-
-
1
def inspect
-
tags = []
-
tags << "trash" if trash
-
tags << "new" if new_card?
-
tags << "frozen" if frozen?
-
tags << "readonly" if readonly?
-
tags << "virtual" if @virtual
-
tags << "set_mods_loaded" if @set_mods_loaded
-
-
error_messages = errors.any? ? "<E*#{errors.full_messages * ', '}*>" : ""
-
-
"#<Card##{id}[#{debug_type}](#{name})#{error_messages}{#{tags * ','}}"
-
end
-
-
1
private
-
-
1
def debug_type
-
21
"#{type_code || ''}:#{type_id}"
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/debug.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (EventConditions)
-
#
-
1
module EventConditions;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/event_conditions.rb"; end
-
1
Card.action_specific_attributes +=
-
%i[skip_hash full_skip_hash trigger_hash full_trigger_hash]
-
-
1
def event_applies? event
-
46379
return unless set_condition_applies? event.set_module, event.opts[:changing]
-
-
15634
Card::Set::Event::CONDITIONS.all? do |key|
-
61714
send "#{key}_condition_applies?", event, event.opts[key]
-
end
-
end
-
-
# force skipping this event for all cards in act
-
1
def skip_event! *events
-
@full_skip_hash = nil
-
events.each do |event|
-
act_skip_hash[event.to_s] = :force
-
end
-
end
-
-
# force skipping this event for this card only
-
1
def skip_event_in_action! *events
-
events.each do |event|
-
full_skip_hash[event.to_s] = :force
-
end
-
end
-
-
# force triggering this event (when it comes up) for all cards in act
-
1
def trigger_event! *events
-
2
@full_trigger_hash = nil
-
2
events.each do |event|
-
2
act_trigger_hash[event.to_s] = :force
-
end
-
end
-
-
# force triggering this event (when it comes up) for this card only
-
1
def trigger_event_in_action! *events
-
events.each do |event|
-
full_trigger_hash[event.to_s] = :force
-
end
-
end
-
-
# hash form of raw skip setting, eg { "my_event" => true }
-
1
def skip_hash
-
796
@skip_hash ||= hash_with_value skip, true
-
end
-
-
1
def trigger_hash
-
80
@trigger_hash ||= hash_with_value trigger, true
-
end
-
-
1
private
-
-
1
def set_condition_applies? set_module, old_sets
-
46379
return true if set_module == Card
-
-
46379
set_condition_card(old_sets).singleton_class.include? set_module
-
end
-
-
1
def on_condition_applies? _event, actions
-
15634
actions = Array(actions).compact
-
15634
actions.empty? ? true : actions.include?(action)
-
end
-
-
# if changing name/type, the old card has no-longer-applicable set modules, so we create
-
# a new card to determine whether events apply.
-
# (note: cached condition card would ideally be cleared after all
-
# conditions are reviewed)
-
# @param old_sets [True/False] whether to use the old_sets
-
1
def set_condition_card old_sets
-
46379
return self if old_sets || no_current_action?
-
34228
@set_condition_card ||=
-
362
updating_sets? ? set_condition_card_with_new_set_modules : self
-
end
-
-
# existing card is being changed in a way that alters its sets
-
1
def updating_sets?
-
362
action == :update && real? && (type_id_is_changing? || name_is_changing?)
-
end
-
-
# prevents locking in set_condition_card
-
1
def no_current_action?
-
44740
return false if @current_action
-
-
10512
@set_condition_card = nil
-
10512
true
-
end
-
-
1
def set_condition_card_with_new_set_modules
-
1
cc = Card.find id
-
1
cc.name = name
-
1
cc.type_id = type_id
-
1
cc.include_set_modules
-
end
-
-
1
def changed_condition_applies? _event, db_columns
-
19527
return true unless action == :update
-
7107
db_columns = Array(db_columns).compact
-
7107
return true if db_columns.empty?
-
3869
db_columns.any? { |col| single_changed_condition_applies? col }
-
end
-
1
alias_method :changing_condition_applies?, :changed_condition_applies?
-
-
1
def when_condition_applies? _event, block
-
8795
case block
-
33
when Proc then block.call(self)
-
4110
when Symbol then send block
-
4652
else true
-
end
-
end
-
-
# "applies always means event can run
-
# so if skip_condition_applies?, we do NOT skip
-
1
def skip_condition_applies? event, allowed
-
8879
return true unless (val = full_skip_hash[event.name.to_s])
-
-
allowed ? val.blank? : (val != :force)
-
end
-
-
1
def trigger_condition_applies? event, required
-
8879
return true unless required
-
-
89
full_trigger_hash[event.name.to_s].present?
-
end
-
-
1
def single_changed_condition_applies? db_column
-
2011
return true unless db_column
-
2011
send "#{db_column}_is_changing?"
-
end
-
-
1
def wrong_stage opts
-
return false if director.stage_ok? opts
-
if !stage
-
"phase method #{method} called outside of event phases"
-
else
-
"#{opts.inspect} method #{method} called in stage #{stage}"
-
end
-
end
-
-
1
def wrong_action actn
-
return false if on_condition_applies?(nil, actn)
-
"on: #{actn} method #{method} called on #{action}"
-
end
-
-
1
def full_skip_hash
-
8879
@full_skip_hash ||= act_skip_hash.merge skip_in_action_hash
-
end
-
-
1
def act_skip_hash
-
796
(act_card || self).skip_hash
-
end
-
-
1
def skip_in_action_hash
-
796
hash_with_value skip_in_action, true
-
end
-
-
1
def full_trigger_hash
-
89
@full_trigger_hash ||= act_trigger_hash.merge trigger_in_action_hash
-
end
-
-
1
def trigger_in_action_hash
-
78
hash_with_value trigger_in_action, true
-
end
-
-
1
def act_trigger_hash
-
80
(act_card || self).trigger_hash
-
end
-
-
1
def hash_with_value array, value
-
1445
Array.wrap(array).each_with_object({}) do |event, hash|
-
3
hash[event.to_s] = value
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/event_conditions.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Export)
-
#
-
1
module Export;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/export.rb"; end
-
2
module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
-
# returns an array of Hashes (each in export_item view)
-
1
view :export, cache: :never do
-
exporting_uniques do
-
Array.wrap(render_export_item).concat(export_items_in_view(:export)).flatten
-
end
-
end
-
-
1
def max_export_depth
-
Env.params[:max_export_depth].present? ? Env.params[:max_export_depth].to_i : 2
-
end
-
-
# returns an array of Hashes (each in export_item view)
-
1
view :export_items, cache: :never do
-
exporting_uniques do
-
export_items_in_view(:export).flatten
-
end
-
end
-
-
# returns Hash with the essentials needed to import a card into a new database
-
1
view :export_item do
-
item = { name: card.name, type: card.type_name, content: card.content }
-
item[:codename] = card.codename if card.codename
-
track_exporting card
-
item
-
end
-
-
1
def export_items_in_view view
-
within_max_depth do
-
valid_items_for_export.map do |item|
-
nest item, view: view
-
end
-
end
-
end
-
-
1
def track_exporting card
-
return unless @exported_keys
-
@exported_keys << card.key
-
end
-
-
1
def exporting_uniques
-
@exported_keys ||= inherit(:exported_keys) || ::Set.new
-
yield
-
end
-
-
# prevent recursion
-
1
def within_max_depth
-
@export_depth ||= inherit(:export_depth).to_i + 1
-
@export_depth > max_export_depth ? [] : yield
-
end
-
-
1
def items_for_export
-
nest_chunks.map do |chunk|
-
next if chunk.try :main?
-
chunk.referee_card
-
end.compact
-
end
-
-
1
def valid_items_for_export
-
items_for_export.flatten.reject(&:blank?).uniq.find_all do |card|
-
valid_export_card? card
-
end
-
end
-
-
1
def valid_export_card? ecard
-
ecard.real? && !@exported_keys.include?(ecard.key)
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/export.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Extended)
-
#
-
1
module Extended;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/extended.rb"; end
-
-
1
def extended_item_cards context=nil
-
1
items = item_cards limit: "", context: (context || self).name
-
1
list = []
-
1
book = ::Set.new # avoid loops
-
1
extend_item_list items, list, book until items.empty?
-
1
list
-
end
-
-
1
def extended_item_contents context=nil
-
extended_item_cards(context).map(&:item_names).flatten
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
delegate :extended_item_contents, to: :card
-
end
-
-
1
private
-
-
1
def extend_item_list items, list, book
-
1
item = items.shift
-
1
return if already_extended? item, book
-
1
if item.collection?
-
# keep items in order
-
items.unshift(*item.item_cards)
-
else # no further level of items
-
1
list << item
-
end
-
end
-
-
1
def already_extended? item, book
-
1
return true if book.include? item
-
1
book << item
-
1
false
-
end
-
-
# def extended_list context=nil
-
# context = (context ? context.name : name)
-
# args = { limit: "" }
-
# item_cards(args.merge(context: context)).map do |x|
-
# x.item_cards(args)
-
# end.flatten.map do |x|
-
# x.item_cards(args)
-
# end.flatten.map do |y|
-
# y.item_names(args)
-
# end.flatten
-
# # this could go on and on. more elegant to recurse until you don't have
-
# # a collection
-
# end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/extended.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Fetch)
-
#
-
# = Card#fetch
-
#
-
1
module Fetch;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/fetch.rb"; end
-
# A multipurpose retrieval operator that integrates caching, database lookups,
-
# and "virtual" card construction
-
1
module ClassMethods
-
# Look for cards in
-
# * cache
-
# * database
-
# * virtual cards
-
#
-
# @param args [Integer, String, Card::Name, Symbol, Array]
-
# one or more of the three unique identifiers
-
# 1. a numeric id (Integer)
-
# 2. a name/key (String or Card::Name)
-
# 3. a codename (Symbol)
-
# If you pass more then one mark or an array of marks they get joined with a '+'.
-
# The final argument can be a hash to set the following options
-
# :skip_virtual Real cards only
-
# :skip_modules Don't load Set modules
-
# :look_in_trash Return trashed card objects
-
# :local_only Use only local cache for lookup and storing
-
# new: { opts for Card#new } Return a new card when not found
-
# @return [Card]
-
1
def fetch *args
-
144896
Card::Fetch.new(*args)&.retrieve_or_new
-
rescue ActiveModel::RangeError => _e
-
return Card.new name: "card id out of range: #{f.mark}"
-
end
-
-
# fetch only real (no virtual) cards
-
#
-
# @param mark - see #fetch
-
# @return [Card]
-
1
def [] *mark
-
3106
fetch(*mark, skip_virtual: true)
-
end
-
-
# fetch real cards without set modules loaded. Should only be used for simple attributes
-
# @example
-
# quick_fetch "A", :self, :structure
-
#
-
# @param mark - see #fetch
-
# @return [Card]
-
1
def quick_fetch *mark
-
93410
fetch(*mark, skip_virtual: true, skip_modules: true)
-
end
-
-
# @return [Card]
-
1
def fetch_from_cast cast
-
fetch_args = cast[:id] ? [cast[:id].to_i] : [cast[:name], { new: cast }]
-
fetch *fetch_args
-
end
-
-
#----------------------------------------------------------------------
-
# ATTRIBUTE FETCHING
-
# The following methods optimize fetching of specific attributes
-
-
1
def id cardish
-
14367
case cardish
-
12658
when Integer then cardish
-
65
when Card then cardish.id
-
when Symbol then Card::Codename.id cardish
-
1644
else fetch_id cardish
-
end
-
end
-
-
# @param mark_parts - see #fetch
-
# @return [Integer]
-
1
def fetch_id *mark_parts
-
74542
mark = Card::Fetch.new(*mark_parts)&.mark
-
74542
mark.is_a?(Integer) ? mark : quick_fetch(mark.to_s)&.id
-
end
-
-
# @param mark - see #fetch
-
# @return [Card::Name]
-
1
def fetch_name *mark
-
6066
if (card = quick_fetch(*mark))
-
6061
card.name
-
5
elsif block_given?
-
yield.to_name
-
end
-
rescue ActiveModel::RangeError => _e
-
block_given? ? yield.to_name : nil
-
rescue Card::Error::CodenameNotFound => e
-
block_given? ? yield.to_name : raise(e)
-
end
-
-
# @param mark - see #fetch
-
# @return [Integer]
-
1
def fetch_type_id mark
-
226
quick_fetch(mark)&.type_id
-
end
-
end
-
-
#----------------------------------------------------------------------
-
# INSTANCE METHODS
-
# fetching from the context of a card
-
-
1
def fetch traits, opts={}
-
3179
opts[:new][:supercard] = self if opts[:new]
-
3179
Array.wrap(traits).inject(self) do |card, trait|
-
3179
Card.fetch card.name.trait(trait), opts
-
end
-
end
-
-
1
def newish opts
-
141
reset_patterns
-
141
Card.with_normalized_new_args opts do |norm_opts|
-
141
handle_type norm_opts do
-
141
assign_attributes norm_opts
-
141
self.name = name # trigger superize_name
-
end
-
end
-
end
-
-
1
def refresh force=false
-
200
return self unless force || frozen? || readonly?
-
return unless id
-
fresh_card = self.class.find id
-
fresh_card.include_set_modules
-
fresh_card
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/fetch.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (FetchHelper)
-
#
-
1
module FetchHelper;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/fetch_helper.rb"; end
-
1
module ClassMethods
-
# a fetch method to support the needs of the card controller.
-
# should be in Decko?
-
1
def controller_fetch args
-
444
card_opts = controller_fetch_opts args
-
444
if args[:action] == "create"
-
# FIXME: we currently need a "new" card to catch duplicates
-
# (otherwise save will just act like a normal update)
-
# We may need a "#create" instance method to handle this checking?
-
54
Card.new card_opts
-
else
-
390
standard_controller_fetch args, card_opts
-
end
-
end
-
-
1
private
-
-
1
def standard_controller_fetch args, card_opts
-
390
mark = args[:mark] || card_opts[:name]
-
390
card = Card.fetch mark, skip_modules: true,
-
look_in_trash: args[:look_in_trash],
-
new: card_opts
-
390
card.assign_attributes card_opts if args[:assign] && card&.real?
-
390
card&.include_set_modules
-
390
card
-
end
-
-
1
def controller_fetch_opts args
-
444
opts = Env.hash args[:card]
-
444
opts[:type] ||= args[:type] if args[:type]
-
# for /new/:type shortcut. we should handle in routing and deprecate this
-
444
opts[:name] ||= Card::Name.url_key_to_standard args[:mark]
-
444
opts
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/fetch_helper.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Haml)
-
#
-
1
module Haml;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/haml.rb"; end
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
include Card::Set::Format::HamlPaths
-
-
1
define_method :the_scope do
-
set_scope
-
end
-
-
1
define_method :haml_scope do
-
set_scope
-
end
-
-
# Renders haml templates. The haml template can be passed as string or
-
# block or a symbol that refers to a view template.
-
# @param args [Hash, String, Symbol]
-
# If a symbol is given then a template is expected in the corresponding view
-
# directory.
-
# @return [String] rendered haml as HTML
-
# @example render a view template
-
# # view/type/basic/my_template.haml:
-
# %p
-
# Hi
-
# = name
-
#
-
# # set/type/basic.rb:
-
# view :my_view do
-
# haml :my_template, name: "Joe: # => "<p>Hi Joe<p/>"
-
# end
-
# @example use a block to pass haml
-
# haml name: "Joe" do
-
# <<-HAML.strip_heredoc
-
# %p
-
# Hi
-
# = name
-
# HAML
-
# # => <p>Hi Joe</p>
-
# @example create a slot in haml code
-
# - haml_wrap do
-
# %p
-
# some haml
-
-
1
def haml *args, &block
-
1266
if args.first.is_a? Symbol
-
1266
process_haml_template(*args)
-
else
-
process_haml(*args, &block)
-
end
-
end
-
-
1
def haml_partial partial, locals={}
-
locals[:template_path] ||= @template_path
-
process_haml_template "_#{partial}".to_sym, locals
-
end
-
-
1
private
-
-
1
def process_haml *args
-
args.unshift yield if block_given?
-
haml_to_html(*args)
-
end
-
-
1
def process_haml_template template_name, *args
-
1266
locals = args.first || {}
-
1266
path = identify_template_path template_name, locals
-
1266
with_template_path path do
-
1266
haml_to_html ::File.read(path), *args
-
end
-
# rescue => e
-
# raise Card::Error, "HAML error #{template_name}: #{e.message}\n#{e.backtrace}"
-
end
-
-
1
def identify_template_path view, locals={}
-
1266
base_path = locals.delete(:template_path) || caller_locations[2].path
-
1266
haml_template_path view, base_path
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/haml.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (I18n)
-
#
-
1
module I18n;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/i18n.rb"; end
-
-
1
def tr key, args={}
-
11
::I18n.t key, args.reverse_merge(scope: Card::Set.scope(caller))
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
def tr key, args={}
-
32
::I18n.t key, args.reverse_merge(scope: Card::Set.scope(caller))
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/i18n.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Initialize)
-
#
-
1
module Initialize;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/initialize.rb"; end
-
1
JUNK_INIT_ARGS = %w[missing skip_virtual id].freeze
-
-
1
module ClassMethods
-
1
def new args={}, _options={}
-
3978
with_normalized_new_args args do |normalized_args|
-
3978
super normalized_args
-
end
-
end
-
-
1
def with_normalized_new_args args={}
-
4119
args = (args || {}).stringify_keys
-
4119
delete_junk_args args
-
4119
normalize_type_args args
-
4119
normalize_content_args args
-
4119
yield args
-
end
-
-
1
private
-
-
1
def delete_junk_args args
-
16476
JUNK_INIT_ARGS.each { |a| args.delete(a) }
-
end
-
-
1
def normalize_type_args args
-
12357
%w[type type_code].each { |k| args.delete(k) if args[k].blank? }
-
end
-
-
1
def normalize_content_args args
-
4119
args.delete("content") if args["attach"] # should not be handled here!
-
4119
args["db_content"] = args.delete "content" if args["content"]
-
end
-
end
-
-
1
def initialize args={}
-
3978
args["name"] = initial_name args["name"]
-
-
3978
handle_set_modules args do
-
3978
handle_type args do
-
3978
super args # ActiveRecord #initialize
-
end
-
end
-
3978
self
-
end
-
-
1
def handle_set_modules args
-
3978
skip_modules = args.delete "skip_modules"
-
3978
yield
-
3978
include_set_modules unless skip_modules
-
end
-
-
1
def handle_type args
-
4119
type_lookup = args["type_lookup"]
-
4119
@supercard = args.delete "supercard"
-
-
4119
yield
-
4119
type_id_from_template if type_lookup == :force || (!type_id && type_lookup != :skip)
-
end
-
-
1
def initial_name name
-
3978
name.is_a?(String) ? name : Card::Name[name].to_s
-
end
-
-
1
def include_set_modules
-
24727
return self if @set_mods_loaded
-
-
15651
set_modules.each do |m|
-
34340
singleton_class.send :include, m
-
end
-
15651
assign_set_specific_attributes
-
15651
@uncacheable = @set_mods_loaded = true
-
15651
self
-
end
-
-
1
def uncacheable?
-
9220
@uncacheable == true
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/initialize.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Item)
-
#
-
1
module Item;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/item.rb"; end
-
1
def item_names _args={}
-
format._render_raw.split(/[,\n]/)
-
end
-
-
1
def item_cards _args={} # FIXME: this is inconsistent with item_names
-
1
[self]
-
end
-
-
1
def item_type
-
nil
-
end
-
-
1
def item_keys args={}
-
item_names(args).map do |item|
-
item.to_name.key
-
end
-
end
-
-
1
def item_count args={}
-
item_names(args).size
-
end
-
-
1
def items_to_content array
-
677
items = array.map { |i| standardize_item i }.reject(&:blank?)
-
42
self.content = items.to_pointer_content
-
end
-
-
1
def standardize_item item
-
635
Card::Name[item]
-
end
-
-
1
def include_item? item
-
item_names.include? Card::Name[item]
-
end
-
-
1
def add_item item
-
return if include_item? item
-
items_to_content(items_strings << item)
-
end
-
-
1
def drop_item item
-
item = Card::Name[item]
-
return unless include_item? item
-
items_to_content(item_names.reject { |i| i == item })
-
end
-
-
1
def insert_item index, name
-
new_names = item_names
-
new_names.delete name
-
new_names.insert index, name
-
items_to_content new_names
-
end
-
-
1
def replace_item old, new
-
return unless include_item? old
-
drop_item old
-
add_item new
-
end
-
-
# I think the following should work as add_item...
-
#
-
1
def add_id id
-
add_item "~#{id}"
-
end
-
-
1
def drop_id id
-
drop_item "~#{id}"
-
end
-
-
1
def insert_id index, id
-
insert_item index, "~#{id}"
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
def item_links _args={}
-
raw(render_core).split(/[,\n]/)
-
end
-
-
1
def nest_item cardish, options={}, &block
-
56
options = item_view_options options
-
56
options[:nest_name] = Card::Name[cardish].s
-
56
nest cardish, options, &block
-
end
-
-
1
def implicit_item_view
-
19
view = voo_items_view || default_item_view
-
19
Card::View.normalize view
-
end
-
-
1
def voo_items_view
-
199
return unless voo && (items = voo.items)
-
199
items[:view]
-
end
-
-
1
def default_item_view
-
:name
-
end
-
-
1
def item_view_options new_options={}
-
56
options = (voo.items || {}).clone
-
56
options = options.merge new_options
-
56
options[:view] ||= implicit_item_view
-
56
determine_item_view_options_type options
-
56
options
-
end
-
-
1
def determine_item_view_options_type options
-
56
return if options[:type]
-
56
type_from_rule = card.item_type
-
56
options[:type] = type_from_rule if type_from_rule
-
end
-
-
1
def listing listing_cards, item_args={}
-
16
listing_cards.map do |item_card|
-
19
nest_item item_card, item_args do |rendered, item_view|
-
19
wrap_item rendered, item_view
-
end
-
end
-
end
-
-
1
def wrap_item item, _args={}
-
item # no wrap in base
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def wrap_item rendered, item_view
-
%(<div class="item-#{item_view}">#{rendered}</div>)
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/item.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Layouts)
-
#
-
1
module Layouts;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/layouts.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
attr_reader :interior
-
-
1
def layout_nest
-
wrap_main { interior }
-
end
-
-
1
layout :pre do # {{_main|raw}}
-
wrap_with :pre do
-
layout_nest
-
end
-
end
-
-
1
layout :simple do
-
layout_nest
-
end
-
-
1
layout :no_side do # {{_main|open}}
-
<<-HTML.strip_heredoc
-
<header>#{nest :header, view: :core}</header>
-
<article>#{layout_nest}</article>
-
<footer>{nest :footer, view: :core}</footer>
-
HTML
-
end
-
-
1
layout :default do
-
<<-HTML.strip_heredoc
-
<header>#{nest :header, view: :core}</header>
-
<article>#{layout_nest}</article>
-
<aside>#{nest :sidebar, view: :core}</aside>
-
<footer>{nest :footer, view: :core}</footer>
-
HTML
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/layouts.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (LocationHistory)
-
#
-
1
module LocationHistory;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/location_history.rb"; end
-
1
event :discard_deleted_locations, :finalize, on: :delete do
-
6
Env.discard_locations_for self
-
6
configure_successful_deletion if success.target == self
-
end
-
-
1
event :save_current_location, before: :show_page, on: :read do
-
315
Env.save_location self
-
end
-
-
# TO DISCUSS: should this default behavior be directly in the controller?
-
# Or at least in decko?
-
1
def configure_successful_deletion
-
1
if Env.ajax?
-
success.card = self
-
success.view = :unknown unless success.view
-
else
-
1
success.target = :previous
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/location_history.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Name)
-
#
-
1
module Name;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/name.rb"; end
-
1
require "uuid"
-
-
1
module ClassMethods
-
1
def uniquify_name name, rename=:new
-
return name unless Card.exists? name
-
uniq_name = generate_alternative_name name
-
return uniq_name unless rename == :old
-
rename!(name, uniq_name)
-
name
-
end
-
-
1
def generate_alternative_name name
-
uniq_name = "#{name} 1"
-
uniq_name.next! while Card.exists?(uniq_name)
-
uniq_name
-
end
-
-
1
def rename! oldname, newname
-
Card[oldname].update! name: newname, update_referers: true
-
end
-
end
-
-
1
def name
-
188027
@name ||= left_id ? Card::Lexicon.lex_to_name([left_id, right_id]) : super.to_name
-
end
-
-
1
def key
-
21928
@key ||= left_id ? name.key : super
-
end
-
-
1
def name= newname
-
4243
@name = superize_name newname.to_name
-
4243
self.key = @name.key
-
4243
update_subcard_names @name
-
4243
write_attribute :name, (@name.simple? ? @name.s : nil)
-
4243
assign_side_ids
-
4243
@name
-
end
-
-
1
def assign_side_ids
-
4243
if name.simple?
-
807
self.left_id = self.right_id = nil
-
else
-
3436
assign_side_id :left_id=, :left_name
-
3436
assign_side_id :right_id=, :right_name
-
end
-
end
-
-
# assigns left_id and right_id based on names.
-
# if side card is new, id is temporarily stored as -1
-
1
def assign_side_id side_id_equals, side_name
-
6872
side_id = Card::Lexicon.id(name.send(side_name)) || -1
-
6872
send side_id_equals, side_id
-
end
-
-
1
def superize_name cardname
-
4243
return cardname unless @supercard
-
88
@raw_name = cardname.s
-
88
@supercard.subcards.rename key, cardname.key
-
88
update_superleft cardname
-
88
cardname.absolute_name @supercard.name
-
end
-
-
1
def update_superleft cardname
-
683
@superleft = @supercard if cardname.field_of? @supercard.name
-
end
-
-
1
def key= newkey
-
4243
return if newkey == key
-
3978
update_cache_key key do
-
3978
write_attribute :key, (name.simple? ? newkey : nil)
-
3978
@key = newkey
-
end
-
3978
clean_patterns
-
3978
@key
-
end
-
-
1
def clean_patterns
-
3978
return unless patterns?
-
reset_patterns
-
patterns
-
end
-
-
1
def update_cache_key oldkey
-
3978
yield
-
3978
was_in_cache = Card.cache.soft.delete oldkey
-
3978
Card.write_to_soft_cache self if was_in_cache
-
end
-
-
1
def update_subcard_names new_name, name_to_replace=nil
-
4243
return unless @subcards
-
2
subcards.each do |subcard|
-
2
update_subcard_name subcard, new_name, name_to_replace if subcard.new?
-
end
-
end
-
-
1
def update_subcard_name subcard, new_name, name_to_replace
-
name_to_replace ||= name_to_replace_for_subcard subcard, new_name
-
subcard.name = subcard.name.swap name_to_replace, new_name.s
-
subcard.update_subcard_names new_name, name # needed? shouldn't #name= trigger this?
-
end
-
-
1
def name_to_replace_for_subcard subcard, new_name
-
# if subcard has a relative name like +C
-
# and self is a subcard as well that changed from +B to A+B then
-
# +C should change to A+B+C. #replace doesn't work in this case
-
# because the old name +B is not a part of +C
-
if subcard.name.starts_with_joint? && new_name.parts.first.present?
-
"".to_name
-
else
-
name
-
end
-
end
-
-
1
def autoname name
-
if Card.exists?(name) || Director.include?(name)
-
autoname name.next
-
else
-
name
-
end
-
end
-
-
# FIXME: use delegations and include all name functions
-
1
def simple?
-
14087
name.simple?
-
end
-
-
1
def junction?
-
1431
name.junction?
-
end
-
-
1
def raw_name
-
@raw_name || name
-
end
-
-
1
def left *args
-
case
-
2834
when simple? then nil
-
87
when superleft then superleft
-
when name_is_changing? && name.to_name.trunk_name == name_before_act.to_name
-
nil # prevent recursion when, eg, renaming A+B to A+B+C
-
else
-
1863
Card.fetch name.left, *args
-
end
-
end
-
-
1
def right *args
-
9780
Card.fetch(name.right, *args) unless simple?
-
end
-
-
1
def [] *args
-
1008
case args[0]
-
when Integer, Range
-
387
fetch_name = Array.wrap(name.parts[args[0]]).compact.join Card::Name.joint
-
387
Card.fetch(fetch_name, args[1] || {}) unless simple?
-
else
-
621
super
-
end
-
end
-
-
1
def trunk *args
-
154
simple? ? self : left(*args)
-
end
-
-
1
def tag *args
-
44
simple? ? self : Card.fetch(name.right, *args)
-
end
-
-
1
def left_or_new args={}
-
239
left(args) || Card.new(args.merge(name: name.left))
-
end
-
-
# NOTE: for all these helpers, method returns *all* fields/children/descendants.
-
# (Not just those current user has permission to read.)
-
-
1
def fields
-
2
field_ids.map { |id| Card[id] }
-
end
-
-
1
def field_names
-
field_ids.map { |id| Card::Name[id] }
-
end
-
-
1
def field_ids
-
1
child_ids :left
-
end
-
-
1
def each_child
-
6
child_ids.each do |id|
-
5
(child = Card[id]) && yield(child)
-
# check should not be needed (remove after fixing data problems)
-
end
-
end
-
-
# eg, A+B is a child of A and B
-
1
def child_ids side=nil
-
7
return [] unless id
-
7
side ||= name.simple? ? :part : :left_id
-
7
Auth.as_bot do
-
7
Card.search({ side => id, return: :id, limit: 0 }, "children of #{name}")
-
end
-
end
-
-
1
def each_descendant &block
-
each_child do |child|
-
yield child
-
child.each_descendant(&block)
-
end
-
end
-
-
1
def right_id= cardish
-
4248
write_card_or_id :right_id, cardish
-
end
-
-
1
def left_id= cardish
-
4303
write_card_or_id :left_id, cardish
-
end
-
-
1
def write_card_or_id attribute, cardish
-
27686
when_id_exists(cardish) { |id| write_attribute attribute, id }
-
end
-
-
1
def when_id_exists cardish, &block
-
13843
if (card_id = Card.id cardish)
-
12185
yield card_id
-
1658
elsif cardish.is_a? Card
-
44
with_id_after_store cardish, &block
-
else
-
1614
yield cardish # eg nil
-
end
-
end
-
-
# subcards are usually saved after super cards;
-
# after_store forces it to save the subcard first
-
# and callback afterwards
-
1
def with_id_after_store subcard
-
44
add_subcard subcard
-
88
subcard.director.after_store { |card| yield card.id }
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/name.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (NameEvents)
-
#
-
# STAGE: prepare to validate
-
1
module NameEvents;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/name_events.rb"; end
-
-
1
event :set_autoname, :prepare_to_validate, on: :create do
-
285
if name.blank? && (autoname_card = rule_card(:autoname))
-
self.name = autoname autoname_card.db_content
-
# FIXME: should give placeholder in approve phase
-
# and finalize/commit change in store phase
-
autoname_card.refresh.update_column :db_content, name
-
end
-
end
-
-
# STAGE: validate
-
-
1
event :validate_name, :validate, on: :save, changed: :name do
-
135
validate_legality_of_name
-
135
return if errors.any?
-
135
Card.write_to_soft_cache self
-
135
validate_uniqueness_of_name
-
end
-
-
1
event :validate_uniqueness_of_name, skip: :allowed do
-
# validate uniqueness of name
-
-
135
return unless (existing_id = Card::Lexicon.id key) && existing_id != id
-
# The above is a fast check but cannot detect if card is in trash
-
-
# TODO: perform the following as a remote-only fetch (not yet supported)
-
return unless (existing_card = Card.where(id: existing_id, trash: false).take)
-
-
errors.add :name, tr(:error_name_exists, name: existing_card.name)
-
end
-
-
1
event :validate_legality_of_name do
-
135
if name.length > 255
-
errors.add :name, tr(:error_too_long, length: name.length)
-
135
elsif name.blank?
-
errors.add :name, tr(:error_blank_name)
-
135
elsif name.parts.include? ""
-
errors.add :name, tr(:is_incomplete)
-
135
elsif !name.valid?
-
errors.add :name, tr(:error_banned_characters, banned: Card::Name.banned_array * " ")
-
135
elsif changing_existing_tag_to_junction?
-
errors.add :name, tr(:error_name_tag, name: name)
-
end
-
end
-
-
1
event :validate_key, after: :validate_name, on: :save do
-
135
if key.empty?
-
errors.add :key, tr(:error_blank_key) if errors.empty?
-
135
elsif key != name.key
-
errors.add :key, tr(:error_wrong_key, key: key, name: name)
-
end
-
end
-
-
# STAGE: store
-
-
1
event :expire_old_name, :store, changed: :name, on: :update do
-
Director.expirees << name_before_act
-
end
-
-
1
event :update_lexicon_on_create, :finalize, changed: :name, on: :create do
-
134
Card::Lexicon.add self
-
end
-
-
1
event :update_lexicon_on_rename, :finalize, changed: :name, on: :update do
-
Card::Lexicon.update self
-
end
-
-
1
def lex
-
134
simple? ? name : [left_id, right_id]
-
end
-
-
1
def old_lex
-
if (old_left_id = left_id_before_act)
-
[old_left_id, right_id_before_act]
-
else
-
name_before_act
-
end
-
end
-
-
1
event :prepare_left_and_right, :store, changed: :name, on: :save do
-
134
return if name.simple?
-
101
prepare_side :left
-
101
prepare_side :right
-
end
-
-
1
def prepare_side side
-
202
side_id = send "#{side}_id"
-
202
sidename = name.send "#{side}_name"
-
202
prepare_obstructed_side(side, side_id, sidename) ||
-
prepare_new_side(side, side_id, sidename)
-
end
-
-
1
def prepare_new_side side, side_id, sidename
-
202
return unless side_id == -1 || !Card[side_id]&.real?
-
-
65
sidecard = Director.card(sidename) || add_subcard(sidename)
-
65
send "#{side}_id=", sidecard
-
end
-
-
1
def prepare_obstructed_side side, side_id, sidename
-
202
return unless side_id && side_id == id
-
clear_name sidename
-
send "#{side}_id=", add_subcard(sidename)
-
true
-
end
-
-
1
private
-
-
1
def changing_existing_tag_to_junction?
-
135
return false unless changing_name_to_junction?
-
name_in_use_as_tag?
-
end
-
-
1
def name_in_use_as_tag?
-
!Card.where(right_id: id, trash: false).take.nil?
-
end
-
-
1
def changing_name_to_junction?
-
135
name.junction? && simple?
-
end
-
-
1
def old_name_in_way? sidecard
-
real? && sidecard&.simple? && id == sidecard&.id
-
end
-
-
1
def clear_name name
-
# move the current card out of the way, in case the new name will require
-
# re-creating a card with the current name, ie. A -> A+B
-
Card.where(id: id).update_all(name: nil, key: nil, left_id: nil, right_id: nil)
-
Card.expire name
-
Card::Lexicon.cache.reset # probably overkill, but this for an edge case...
-
# Card::Lexicon.delete id, key
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/name_events.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Observer)
-
#
-
1
module Observer;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/observer.rb"; end
-
1
%i[create update delete].each do |action|
-
3
event "observer_#{action}".to_sym, :integrate, on: action do
-
216
execute_card_events on: action
-
end
-
end
-
-
1
event :cache_delete_card_events, :store, on: :delete do
-
6
@card_event_cache = event_cards :on_delete
-
end
-
-
1
def execute_card_events args
-
216
setting = "on_#{args[:on]}".to_sym
-
216
event_cards(setting).each do |event_card|
-
1
event_card.deliver self
-
end
-
end
-
-
1
def event_cards setting
-
222
@card_event_cache ||
-
216
((event_rule = rule_card(setting)) && event_rule.extended_item_cards) ||
-
[]
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/observer.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Pattern)
-
#
-
1
module Pattern;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/pattern.rb"; end
-
1
def patterns?
-
23161
defined? @patterns
-
end
-
-
1
def all_patterns
-
105951
@all_patterns ||= set_patterns.map { |sub| sub.new self }.compact
-
end
-
-
# new cards do not
-
1
def patterns
-
14366
@patterns ||= (new_card? ? all_patterns[1..-1] : all_patterns)
-
end
-
-
1
def reset_patterns
-
# Rails.logger.info "resetting patterns: #{name}"
-
6218
@patterns = @all_patterns = nil
-
6218
@template = @virtual = nil
-
6218
@set_mods_loaded = @set_modules = @set_names = @rule_set_keys = nil
-
6218
@junction_only = nil # only applies to set cards
-
6218
true
-
end
-
-
1
def safe_set_keys
-
1510
patterns.map(&:safe_key).reverse * " "
-
end
-
-
1
def set_modules
-
15651
@set_modules ||= all_patterns[0..-2].reverse.map(&:module_list).flatten.compact
-
end
-
-
1
def set_format_modules klass
-
14456
@set_format_modules ||= {}
-
14456
@set_format_modules[klass] =
-
all_patterns[0..-2].reverse.map do |pattern|
-
52132
pattern.format_module_list klass
-
end.flatten.compact
-
end
-
-
1
def set_names
-
169
@set_names = patterns.map(&:to_s) if @set_names.nil?
-
169
@set_names
-
end
-
-
1
def in_set? set_module
-
patterns.map(&:module_key).include? set_module.shortname
-
end
-
-
1
def rule_set_keys
-
18947
@rule_set_keys ||= patterns.map(&:rule_set_key).compact
-
end
-
-
1
def include_module? set
-
singleton_class&.include? set
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/pattern.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Permissions)
-
#
-
1
module Permissions;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/permissions.rb"; end
-
1
module ClassMethods
-
1
def repair_all_permissions
-
Card.where("(read_rule_class is null or read_rule_id is null) and trash is false")
-
.each do |broken_card|
-
broken_card.include_set_modules
-
broken_card.repair_permissions!
-
end
-
end
-
end
-
-
1
def repair_permissions!
-
rule_id, rule_class = permission_rule_id_and_class :read
-
update_columns read_rule_id: rule_id, read_rule_class: rule_class
-
end
-
-
# ok? and ok! are public facing methods to approve one action at a time
-
#
-
# fetching: if the optional :trait parameter is supplied, it is passed
-
# to fetch and the test is perfomed on the fetched card, therefore:
-
#
-
# trait: :account would fetch this card plus a tag codenamed :account
-
# trait: :roles, new: {} would initialize a new card with default ({})
-
# options.
-
-
1
def ok? action
-
8472
@ok ||= {}
-
8472
aok = @ok[Auth.as_id] ||= {}
-
8472
if (cached = aok[action])
-
2787
cached
-
else
-
5685
aok[action] = send "ok_to_#{action}"
-
end
-
end
-
-
1
def ok! action
-
raise Card::Error::PermissionDenied, self unless ok? action
-
end
-
-
1
def who_can action
-
303
permission_rule_card(action).item_cards.map(&:id)
-
end
-
-
1
def anyone_can? action
-
who_can(action).include? AnyoneID
-
end
-
-
1
def direct_rule_card action
-
136
direct_rule_id = rule_card_id action
-
136
require_permission_rule! direct_rule_id, action
-
136
Card.quick_fetch direct_rule_id
-
end
-
-
1
def permission_rule_id action
-
675
if junction? && rule(action).match?(/^\[?\[?_left\]?\]?$/)
-
235
left_permission_rule_id action
-
else
-
440
rule_card_id(action)
-
end
-
end
-
-
1
def permission_rule_id_and_class action
-
136
[permission_rule_id(action), direct_rule_card(action).rule_class_name]
-
end
-
-
1
def left_permission_rule_id action
-
239
lcard = left_or_new(skip_virtual: true, skip_modules: true)
-
239
if action == :create && lcard.real? && lcard.action != :create
-
18
action = :update
-
end
-
239
lcard.permission_rule_id action
-
end
-
-
1
def permission_rule_card action
-
303
Card.fetch permission_rule_id(action)
-
end
-
-
1
def require_permission_rule! rule_id, action
-
136
return if rule_id
-
# RULE missing. should not be possible.
-
# generalize this to handling of all required rules
-
errors.add :permission_denied, tr(:error_no_action_rule, action: action, name: name)
-
raise Card::Error::PermissionDenied, self
-
end
-
-
1
def rule_class_name
-
136
trunk.type_id == SetID ? name.trunk_name.tag : nil
-
end
-
-
1
def you_cant what
-
17
"You don't have permission to #{what}"
-
end
-
-
1
def deny_because why
-
17
@permission_errors << why if @permission_errors
-
17
false
-
end
-
-
1
def permitted? action
-
1200
return false if Card.config.read_only # :read does not call #permit
-
1200
return true if Auth.always_ok?
-
-
298
Auth.as_card.among? who_can(action)
-
end
-
-
1
def permit action, verb=nil
-
# not called by ok_to_read
-
1192
if Card.config.read_only
-
deny_because "Currently in read-only mode"
-
return false
-
end
-
-
1192
return true if permitted? action
-
17
verb ||= action.to_s
-
17
deny_because you_cant("#{verb} #{name.present? ? name : 'this'}")
-
end
-
-
1
def ok_to_create
-
845
return false unless permit :create
-
840
return true if simple?
-
-
181
%i[left right].each do |side|
-
# left is supercard; create permissions will get checked there.
-
362
next if side == :left && superleft
-
275
part_card = send side, new: {}
-
# if no card, there must be other errors
-
275
next unless part_card && part_card.new_card?
-
58
unless part_card.ok? :create
-
deny_because you_cant("create #{part_card.name}")
-
return false
-
end
-
end
-
181
true
-
end
-
-
1
def ok_to_read
-
3699
return true if Auth.always_ok?
-
-
1647
self.read_rule_id ||= permission_rule_id :read
-
1647
return true if Auth.as_card.read_rules_hash[read_rule_id]
-
-
deny_because you_cant "read this"
-
end
-
-
1
def ok_to_update
-
336
return false unless permit(:update)
-
327
return true unless type_id_changed? && !permitted?(:create)
-
deny_because you_cant("change to this type (need create permission)")
-
end
-
-
1
def ok_to_delete
-
11
permit :delete
-
end
-
-
# don't know why we introduced this
-
# but we have to preserve read rules to make
-
# delete acts visible in recent changes -pk
-
# event :clear_read_rule, :store, on: :delete do
-
# self.read_rule_id = self.read_rule_class = nil
-
# end
-
-
1
event :set_read_rule, :store,
-
on: :save, changed: %i[type_id name] do
-
135
read_rule_id, read_rule_class = permission_rule_id_and_class(:read)
-
135
self.read_rule_id = read_rule_id
-
135
self.read_rule_class = read_rule_class
-
end
-
-
1
event :set_field_read_rules,
-
after: :set_read_rule, on: :update, changed: :type_id do
-
# find all cards with me as trunk and update their read_rule
-
# (because of *type plus right)
-
# skip if name is updated because will already be resaved
-
-
1
each_field_as_bot do |field|
-
1
field.refresh.update_read_rule
-
end
-
end
-
-
1
def update_field_read_rules
-
1
return unless type_id_changed? || read_rule_id_changed?
-
each_field_as_bot do |field|
-
field.update_read_rule if field.rule(:read) == "_left"
-
end
-
end
-
-
1
def each_field_as_bot
-
1
Auth.as_bot do
-
2
fields.each { |field| yield field }
-
end
-
end
-
-
1
def without_timestamps
-
1
Card.record_timestamps = false
-
1
yield
-
ensure
-
1
Card.record_timestamps = true
-
end
-
-
1
event :update_read_rule do
-
1
without_timestamps do
-
1
reset_patterns # why is this needed?
-
1
rcard_id, rclass = permission_rule_id_and_class :read
-
# these two are just to make sure vals are correct on current object
-
1
self.read_rule_id = rcard_id
-
1
self.read_rule_class = rclass
-
1
Card.where(id: id).update_all read_rule_id: rcard_id,
-
read_rule_class: rclass
-
1
expire :hard
-
1
update_field_read_rules
-
end
-
end
-
-
1
def add_to_read_rule_update_queue updates
-
@read_rule_update_queue = Array.wrap(@read_rule_update_queue).concat updates
-
end
-
-
1
event :check_permissions, :validate do
-
297
track_permission_errors do
-
297
ok? action_for_permission_check
-
end
-
end
-
-
1
def action_for_permission_check
-
297
commenting? ? :update : action
-
end
-
-
1
def track_permission_errors
-
297
@permission_errors = []
-
297
result = yield
-
300
@permission_errors.each { |msg| errors.add :permission_denied, msg }
-
297
@permission_errors = nil
-
297
result
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/permissions.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (References)
-
#
-
# frozen_string_literal: true
-
-
# Cards can refer to other cards in their content, eg via links and nests.
-
1
module References;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/references.rb"; end
-
# The card that refers is the "referer", the card that is referred to is
-
# the "referee". The reference itself has its own class (Card::Reference),
-
# which handles id-based reference tracking.
-
-
1
PARTIAL_REF_CODE = "P".freeze
-
-
# cards that refer to self
-
1
def referers
-
76
referer_cards_from_references references_in
-
end
-
-
# cards that include self
-
1
def nesters
-
referer_cards_from_references references_in.where(ref_type: "I")
-
end
-
-
1
def referer_cards_from_references references
-
76
references.map(&:referer_id).uniq.map(&Card.method(:fetch)).compact
-
end
-
-
# cards that self refers to
-
1
def referees
-
referees_from_references references_out
-
end
-
-
# cards that self includes
-
1
def nestees
-
referees_from_references references_out.where(ref_type: "I")
-
end
-
-
1
def referees_from_references references
-
references.map(&:referee_key).uniq.map { |key| Card.fetch key, new: {} }
-
end
-
-
# cards that refer to self by name
-
# (finds cards not yet linked by id)
-
1
def name_referers
-
Card.joins(:references_out).where card_references: { referee_key: key }
-
end
-
-
# replace references in card content
-
1
def replace_reference_syntax old_name, new_name
-
obj_content = Card::Content.new content, self
-
obj_content.find_chunks(Card::Content::Chunk::Reference).select do |chunk|
-
next unless (old_ref_name = chunk.referee_name)
-
next unless (new_ref_name = old_ref_name.swap old_name, new_name)
-
chunk.referee_name = chunk.replace_reference old_name, new_name
-
refs = Card::Reference.where referee_key: old_ref_name.key
-
refs.update_all referee_key: new_ref_name.key
-
end
-
-
obj_content.to_s
-
end
-
-
# delete old references from this card's content, create new ones
-
1
def update_references_out
-
260
delete_references_out
-
260
create_references_out
-
end
-
-
# interpret references from this card's content and
-
# insert entries in reference table
-
1
def create_references_out
-
260
ref_hash = {}
-
260
each_reference_out do |referee_name, ref_type|
-
675
interpret_reference ref_hash, referee_name, ref_type
-
end
-
260
return if ref_hash.empty?
-
179
Card::Reference.mass_insert reference_values_array(ref_hash)
-
end
-
-
# delete references from this card
-
1
def delete_references_out
-
266
raise "id required to delete references" if id.nil?
-
266
Card::Reference.where(referer_id: id).delete_all
-
end
-
-
# interpretation phase helps to prevent duplicate references
-
# results in hash like:
-
# { referee1_key: [referee1_id, referee1_type2],
-
# referee2_key...
-
# }
-
1
def interpret_reference ref_hash, raw_referee_name, ref_type
-
833
with_normalized_referee raw_referee_name do |referee_name, referee_key, referee_id|
-
747
ref_hash[referee_key] ||= [referee_id]
-
747
ref_hash[referee_key] << ref_type
-
747
interpret_partial_references ref_hash, referee_name unless referee_id
-
end
-
end
-
-
# Partial references are needed to track references to virtual cards.
-
# For example a link to virual card [[A+*self]] won't have a referee_id,
-
# but when A's name is changed we have to find and update that link.
-
1
def interpret_partial_references ref_hash, referee_name
-
99
return if referee_name.simple?
-
79
[referee_name.left, referee_name.right].each do |sidename|
-
158
interpret_reference ref_hash, sidename, PARTIAL_REF_CODE
-
end
-
end
-
-
# translate interpreted reference hash into values array,
-
# removing duplicate and unnecessary ref_types
-
1
def reference_values_array ref_hash
-
179
values = []
-
179
ref_hash.each do |referee_key, hash_val|
-
747
referee_id = hash_val.shift || "null"
-
747
ref_types = hash_val.uniq
-
747
ref_types.delete PARTIAL_REF_CODE if ref_types.size > 1
-
# partial references are not necessary if there are explicit references
-
747
ref_types.each do |ref_type|
-
747
values << [id, referee_id, "'#{referee_key}'", "'#{ref_type}'"]
-
end
-
end
-
179
values
-
end
-
-
# invokes the given block for each reference in content with
-
# the reference name and reference type
-
1
def each_reference_out
-
207
content_object.find_chunks(Card::Content::Chunk::Reference).each do |chunk|
-
200
yield chunk.referee_name, chunk.reference_code
-
end
-
end
-
-
1
def has_nests?
-
content_object.has_chunk? Card::Content::Chunk::Nest
-
end
-
-
1
def content_object
-
207
Card::Content.new content, self
-
end
-
-
1
protected
-
-
# test for updating referer content
-
1
event :prepare_referer_update, :validate, on: :update, changed: :name do
-
self.update_referers = ![nil, false, "false"].member?(update_referers)
-
end
-
-
# on rename, update names in cards that refer to self by name (as directed)
-
1
event :update_referer_content, :finalize, on: :update, when: :update_referers do
-
referers.each do |card|
-
next if card.structure
-
card.skip_event! :validate_renaming, :check_permissions
-
card.content = card.replace_reference_syntax name_before_act, name
-
attach_subcard card
-
end
-
end
-
-
# on rename, when NOT updating referer content, update references to ensure
-
# that partial references are correctly tracked
-
# eg. A links to X+Y. if X+Y is renamed and we're not updating the link in A,
-
# then we need to be sure that A has a partial reference
-
1
event :update_referer_references_out, :finalize,
-
on: :update, when: :not_update_referers do
-
76
referers.map(&:update_references_out)
-
end
-
-
# when name changes, update references to card
-
1
event :refresh_references_in, :finalize, on: :save do
-
210
Card::Reference.unmap_referees id if action == :update && !update_referers
-
210
Card::Reference.map_referees key, id
-
end
-
-
# when content changes, update references to other cards
-
1
event :refresh_references_out, :finalize, on: :save, changed: :content do
-
182
update_references_out
-
end
-
-
# clean up reference table when card is deleted
-
1
event :clear_references, :finalize, on: :delete do
-
6
delete_references_out
-
6
Card::Reference.unmap_referees id
-
end
-
-
1
def not_update_referers
-
76
!update_referers
-
end
-
-
1
private
-
-
1
def with_normalized_referee referee_name
-
833
return unless referee_name # eg commented nest has no referee_name
-
833
referee_name = referee_name.to_name
-
833
referee_key = referee_name.key
-
833
return if referee_key == key # don't create self reference
-
747
yield referee_name, referee_key, Card::Lexicon.id(referee_name)
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/references.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Rename)
-
#
-
1
module Rename;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/rename.rb"; end
-
1
event :rename_in_trash, after: :expire_old_name, on: :update do
-
existing_card = Card.find_by_key_and_trash name.key, true
-
return if !existing_card || existing_card == self
-
existing_card.name = existing_card.name + "*trash"
-
existing_card.rename_in_trash_without_callbacks
-
existing_card.save!
-
end
-
-
1
event :validate_renaming, :validate, on: :update, changed: :name, skip: :allowed do
-
return if name_before_act&.to_name == name # just changing to new variant
-
errors.add :content, tr(:cannot_change_content) if content_is_changing?
-
errors.add :type, tr(:cannot_change_type) if type_is_changing?
-
detect_illegal_compound_names
-
end
-
-
1
event :cascade_name_changes, :finalize, on: :update, changed: :name do
-
each_descendant do |d|
-
d.action = :update
-
update_referers ? d.update_referers : d.update_referer_references_out
-
d.refresh_references_in
-
d.refresh_references_out
-
d.expire
-
end
-
end
-
-
1
def changed_from_simple_to_compound?
-
name.compound? && name_before_act.to_name.simple?
-
end
-
-
1
def detect_illegal_compound_names
-
return unless changed_from_simple_to_compound? && child_ids(:right).present?
-
errors.add :name, "illegal name change; existing names end in +#{name_before_act}"
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/rename.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Rules)
-
#
-
1
module Rules;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/rules.rb"; end
-
1
def rule setting_code
-
736
rule_card(setting_code, skip_modules: true)&.db_content
-
end
-
-
1
def rule_card setting_code, options={}
-
18006
Card.fetch rule_card_id(setting_code), options
-
end
-
-
1
def rule_card_id setting_code
-
18582
rule_id_lookup Card::Rule.rule_cache, setting_code
-
end
-
-
1
def preference setting_code, user=nil
-
365
preference_card(setting_code, user, skip_modules: true)&.db_content
-
end
-
-
1
def preference_card setting_code, user=nil, options={}
-
365
Card.fetch preference_card_id(setting_code, user), options
-
end
-
-
1
def preference_card_id setting_code, user=nil
-
365
return unless (user_id = preference_user_id user)
-
365
rule_id_lookup Card::Rule.preference_cache,
-
"#{setting_code}+#{user_id}",
-
"#{setting_code}+#{AllID}"
-
end
-
-
1
def is_rule?
-
7890
is_standard_rule? || is_preference?
-
end
-
-
1
def is_standard_rule?
-
7890
(r = right(skip_modules: true)) &&
-
r.type_id == SettingID &&
-
697
(l = left(skip_modules: true)) &&
-
l.type_id == SetID
-
end
-
-
1
def is_preference?
-
7459
name.parts.length > 2 &&
-
1218
(r = right(skip_modules: true)) &&
-
r.type_id == SettingID &&
-
184
(set = self[0..-3, skip_modules: true]) &&
-
set.type_id == SetID &&
-
182
(user = self[-2, skip_modules: true]) &&
-
182
(user.type_id == UserID || user.codename == :all)
-
end
-
-
# FIXME: move to a better place (if still needed) and use codenames
-
1
def related_sets with_self=false
-
# refers to sets that users may configure from the current card -
-
# NOT to sets to which the current card belongs
-
-
sets = []
-
sets << ["#{name}+*self", Card::Set::Self.label(name)] if with_self
-
if known? && name.simple?
-
sets << ["#{name}+*right", Card::Set::Right.label(name)]
-
end
-
sets
-
end
-
-
1
private
-
-
1
def preference_user_id user
-
365
case user
-
365
when Integer then user;
-
when Card then user
-
when nil then Auth.current_id
-
else
-
raise Card::ServerError, "invalid preference user"
-
end
-
end
-
-
1
def rule_id_lookup lookup_hash, cache_suffix, fallback_suffix=nil
-
18947
rule_set_keys.each do |rule_set_key|
-
76482
rule_id = lookup_hash["#{rule_set_key}+#{cache_suffix}"]
-
76482
rule_id ||= fallback_suffix && lookup_hash["#{rule_set_key}+#{fallback_suffix}"]
-
76482
return rule_id if rule_id
-
end
-
nil
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/rules.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (States)
-
#
-
# All cards have one (and only one) of these three states: real, virtual, and unknown.
-
#
-
# - *real* cards are stored in the database (but not in the trash) and have a unique id.
-
# - *virtual* cards are not real, but they act real based on rules. For example,
-
# Home+*editors does a search for all the users who have edited the "Home" card.
-
# There are many other similar cards that search for things like references, children,
-
# etc. But we don't store all these cards in the database; we generate them dynamically
-
1
module States;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/states.rb"; end
-
# based on the names.
-
# - *unknown* cards are everything else.
-
#
-
# These states are frequently grouped as follows:
-
#
-
# - *known* cards are either _real_ or _virtual_
-
# - *new* (or *unreal*) cards are either _unknown_ or _virtual_
-
-
1
module ClassMethods
-
1
def real? mark
-
412
quick_fetch(mark).present?
-
end
-
1
alias exist? real?
-
1
alias exists? real?
-
-
1
def known? mark
-
2623
fetch(mark).present?
-
end
-
end
-
-
# @return [Symbol] :real, :virtual, or :unknown
-
1
def state anti_fishing=true
-
case
-
12
when !known? then :unknown
-
when anti_fishing && !ok?(:read) then :unknown
-
4
when real? then :real
-
when virtual? then :virtual
-
else :wtf
-
end
-
end
-
-
# @return [True/False]
-
1
def real?
-
11165
!unreal?
-
end
-
-
# Virtual cards are structured, compound cards that are not stored in the database. You
-
# can create virtual cards with structure rules.
-
#
-
# Some cards with hard-coded content will also override the #virtual? method. This
-
# is established practice, but it is NOT advisable to override any of the other
-
# state methods.
-
#
-
# @return [True/False]
-
1
def virtual?
-
1295
if @virtual.nil?
-
876
@virtual = real? || name.simple? ? false : structure.present?
-
end
-
1295
@virtual
-
end
-
-
# @return [True/False]
-
1
def unknown?
-
578
!known?
-
end
-
-
# @return [True/False]
-
1
def known?
-
9269
real? || virtual?
-
end
-
-
# @return [True/False]
-
1
def new?
-
251794
new_record? || # not yet in db (from ActiveRecord)
-
!@from_trash.nil? # in process of restoration from trash
-
end
-
1
alias new_card? new?
-
1
alias unreal? new?
-
-
# has not been edited directly by human users. bleep blorp.
-
1
def pristine?
-
new_card? || !user_changes?
-
end
-
-
1
def user_changes?
-
actions.joins(:act).where("card_acts.actor_id != ?", WagnBotID).exists?
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/states.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Subcards)
-
#
-
1
module Subcards;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/subcards.rb"; end
-
1
def field tag, opts={}
-
45
Card.fetch name.field(tag), opts
-
end
-
-
1
def subcard card_name
-
subcards.card card_name
-
end
-
-
1
def subfield field_name
-
147
subcards.field field_name
-
end
-
-
1
def field? tag
-
3
field(tag) || subfield(tag)
-
end
-
-
1
def subcards
-
4654
@subcards ||= Card::Subcards.new self
-
end
-
-
1
def subcards?
-
26
subcards.present?
-
end
-
-
1
def expire_subcards
-
subcards.clear
-
end
-
-
# phase_method :attach_subcard, before: :store do |name_or_card, args=nil|
-
# TODO: handle differently in different stages
-
1
def add_subcard name_or_card, args={}
-
132
subcards.add name_or_card, args
-
end
-
1
alias_method :attach_subcard, :add_subcard
-
-
1
def add_subcard! name_or_card, args={}
-
subcard = subcards.add name_or_card, args
-
subcard.director.reset_stage
-
subcard
-
end
-
1
alias_method :attach_subcard!, :add_subcard!
-
-
# phase_method :attach_subfield, before: :approve do |name_or_card, args=nil|
-
1
def attach_subfield name_or_card, args={}
-
7
subcards.add_field name_or_card, args
-
end
-
1
alias_method :add_subfield, :attach_subfield
-
-
1
def attach_subfield! name_or_card, args={}
-
subcard = subcards.add_field name_or_card, args
-
subcard.director.reset_stage
-
subcard
-
end
-
-
1
def detach_subcard name_or_card
-
24
subcards.remove name_or_card
-
end
-
1
alias_method :remove_subcard, :detach_subcard
-
-
1
def detach_subfield name_or_card
-
subcards.remove_field name_or_card
-
end
-
1
alias_method :remove_subfield, :detach_subfield
-
-
1
def clear_subcards
-
subcards.clear
-
end
-
-
# ensures subfield is present
-
# does NOT override subfield content if already present
-
1
def ensure_subfield field_name, args={}
-
if subfield_present? field_name
-
subfield field_name
-
else
-
add_subfield field_name, args
-
end
-
end
-
-
1
def subfield_present? field_name
-
subfield(field_name)&.content&.present?
-
end
-
-
1
def deep_clear_subcards
-
subcards.deep_clear
-
end
-
-
1
def handle_subcard_errors
-
2904
subcards.each do |subcard|
-
1224
subcard.errors.each do |field, err|
-
subcard_error subcard, field, err
-
end
-
1224
subcard.errors.clear
-
end
-
end
-
-
1
def subcard_error subcard, field, err
-
err = "#{field} #{err}" unless %i[content abort].member? field
-
errors.add subcard.name.from(name), err
-
end
-
-
1
event :reject_empty_subcards, :prepare_to_validate do
-
451
subcards.each_with_key do |subcard, key|
-
201
next unless subcard.new? && subcard.unfilled?
-
24
remove_subcard(key)
-
24
director.subdirectors.delete(subcard)
-
end
-
end
-
-
# check when deleting field that left has not also been deleted
-
1
def trashed_left?
-
l = left
-
!l || l.trash
-
end
-
-
# check when renaming field that it is not actually the same field
-
# (eg on a renamed trunk)
-
1
def same_field?
-
(left_id == left_id_before_act) && (right_id == right_id_before_act)
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/subcards.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Tabs)
-
#
-
1
module Tabs;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/tabs.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :tabs do
-
construct_tabs "tabs"
-
end
-
-
1
def construct_tabs tab_type
-
tabs = { active: {}, paths: {} }
-
voo.items[:view] ||= :content
-
card.each_item_name_with_options(_render_raw) do |name, options|
-
construct_tab tabs, name, options
-
end
-
tabs tabs[:paths], tabs[:active][:name], tab_type: tab_type, load: :lazy do
-
tabs[:active][:content]
-
end
-
end
-
-
1
def construct_tab tabs, name, explicit_options
-
tab_options = item_view_options explicit_options
-
tabs[:paths][name] = {
-
title: nest(name, view: :title, title: tab_options[:title]),
-
path: nest_path(name, tab_options).html_safe
-
}
-
return unless tabs[:active].empty?
-
tabs[:active] = { name: name, content: nest(name, tab_options) }
-
end
-
-
# def tab_title title, name
-
# return name unless title
-
# name.to_name.title title, @context_names
-
# end
-
-
1
view :pills do
-
construct_tabs "pills"
-
end
-
-
1
view :tabs_static do
-
construct_static_tabs "tabs"
-
end
-
-
1
view :pills_static do
-
construct_static_tabs "pills"
-
end
-
-
1
def construct_static_tabs tab_type
-
tabs = {}
-
card.item_cards.each do |item|
-
tabs[item.name] = nest item, item_view_options(args)
-
end
-
tabs tabs, nil, tab_type: tab_type
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/tabs.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Templating)
-
#
-
1
module Templating;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/templating.rb"; end
-
-
1
def is_template?
-
return @is_template unless @is_template.nil?
-
-
@is_template = name.trait_name? :structure, :default
-
end
-
-
1
def is_structure?
-
3229
return @is_structure unless @is_structure.nil?
-
-
1400
@is_structure = name.trait_name? :structure
-
end
-
-
1
def template
-
# currently applicable templating card.
-
# note that a *default template is never returned for an existing card.
-
17423
@template ||= begin
-
11688
@virtual = false
-
-
11688
if new_card?
-
2818
new_card_template
-
else
-
8870
structure_rule_card
-
end
-
end
-
end
-
-
1
def default_type_id
-
62
Card.default_type_id
-
end
-
-
1
def new_card_template
-
2818
default = rule_card :default, skip_modules: true
-
-
2818
dup_card = dup
-
2818
dup_card.type_id = default&.type_id || default_type_id
-
-
2818
if (structure = dup_card.structure_rule_card)
-
744
@virtual = true if junction?
-
744
self.type_id = structure.type_id if assign_type_to?(structure)
-
744
structure
-
else
-
2074
default
-
end
-
end
-
-
1
def assign_type_to? structure
-
744
return if type_id == structure.type_id
-
430
structure.assigns_type?
-
end
-
-
1
def assigns_type?
-
# needed because not all *structure templates govern the type of set members
-
# for example, X+*type+*structure governs all cards of type X,
-
# but the content rule does not (in fact cannot) have the type X.
-
477
pattern_code = Card.quick_fetch(name.trunk_name.tag_name)&.codename
-
477
return unless pattern_code && (set_class = Set::Pattern.find pattern_code)
-
-
477
set_class.assigns_type
-
end
-
-
1
def structure
-
11750
return unless template && template.is_structure?
-
676
template
-
end
-
-
1
def structure_rule_card
-
11688
return unless (card = rule_card :structure, skip_modules: true)
-
-
877
card.db_content&.strip == "_self" ? nil : card
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/templating.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Trash)
-
#
-
1
module Trash;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/trash.rb"; end
-
1
Self::Admin.add_to_basket(
-
:tasks,
-
name: :empty_trash,
-
irreversible: true,
-
execute_policy: -> { Card.empty_trash },
-
stats: {
-
title: "trashed cards",
-
count: -> { Card.where(trash: true) },
-
link_text: "empty trash",
-
task: "empty_trash"
-
}
-
)
-
-
1
module ClassMethods
-
1
def empty_trash
-
Card.delete_trashed_files
-
Card.where(trash: true).in_batches.update_all(left_id: nil, right_id: nil)
-
Card.where(trash: true).in_batches.delete_all
-
Card::Action.delete_cardless
-
Card::Change.delete_actionless
-
Card::Act.delete_actionless
-
Card::Reference.unmap_if_referee_missing
-
Card::Reference.delete_if_referer_missing
-
end
-
-
# deletes any file not associated with a real card.
-
1
def delete_trashed_files
-
dir = Cardio.paths["files"].existent.first
-
# TODO: handle cloud files
-
return unless dir
-
-
trashed_card_ids = all_trashed_card_ids
-
file_ids = all_file_ids
-
file_ids.each do |file_id|
-
next unless trashed_card_ids.member?(file_id)
-
raise Card::Error, tr(:exception_almost_deleted) if Card.exists?(file_id)
-
::FileUtils.rm_rf "#{dir}/#{file_id}", secure: true
-
end
-
end
-
-
1
def all_file_ids
-
dir = Card.paths["files"].existent.first
-
Dir.entries(dir)[2..-1].map(&:to_i)
-
end
-
-
1
def all_trashed_card_ids
-
trashed_card_sql = %( select id from cards where trash is true )
-
sql_results = Card.connection.select_all(trashed_card_sql)
-
sql_results.map(&:values).flatten.map(&:to_i)
-
end
-
end
-
-
1
def delete args={}
-
add_to_trash args do |delete_args|
-
update delete_args
-
end
-
end
-
-
1
def delete! args={}
-
7
add_to_trash args do |delete_args|
-
7
update! delete_args
-
end
-
end
-
-
1
def add_to_trash args
-
7
return if new_card?
-
7
yield args.merge trash: true
-
end
-
-
1
event :manage_trash, :prepare_to_store, on: :create do
-
134
pull_from_trash!
-
134
self.trash = false
-
134
true
-
end
-
-
1
def pull_from_trash!
-
134
return unless (id = Card::Lexicon.id key) # name is already known
-
return unless (trashed_card = Card.where(id: id).take)&.trash
-
# confirm name is actually in trash
-
-
db_attributes["id"] = trashed_card.db_attributes["id"]
-
# id_in_database returns existing card id
-
-
@from_trash = true
-
@new_record = false
-
end
-
-
1
def db_attributes
-
send(:mutations_from_database).send :attributes
-
end
-
-
1
event :validate_delete, :validate, on: :delete do
-
12
unless codename.blank?
-
6
errors.add :delete, tr(:error_system_card, name: name, codename: codename)
-
end
-
-
undeletable_all_rules_tags =
-
12
%w[default style layout create read update delete]
-
# FIXME: HACK! should be configured in the rule
-
-
12
if junction? && left&.codename == :all &&
-
undeletable_all_rules_tags.member?(right.codename.to_s)
-
errors.add :delete, tr(:error_indestructible, name: name)
-
end
-
-
12
errors.add :delete, tr(:error_user_edits, name: name) if account && has_edits?
-
end
-
-
1
event :validate_delete_children, after: :validate_delete, on: :delete do
-
12
return if errors.any?
-
6
each_child do |child|
-
5
next unless child
-
# prevents errors in cases where a child is deleted prior to this point
-
# and thus is not returned by the fetch in #children
-
-
5
delete_as_subcard child
-
# next if child.valid?
-
# child.errors.each do |field, message|
-
# errors.add field, "can't delete #{child.name}: #{message}"
-
# end
-
end
-
end
-
-
1
def delete_as_subcard subcard
-
5
subcard.trash = true
-
5
add_subcard subcard
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/trash.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Type)
-
#
-
1
module Type;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/type.rb"; end
-
-
1
module ClassMethods
-
1
def default_type_id
-
72
@@default_type_id ||= Card[:all].fetch(:default, skip_modules: true).type_id
-
end
-
end
-
-
1
def type_card
-
11199
return if type_id.nil?
-
11199
Card.quick_fetch type_id.to_i
-
end
-
-
1
def type_code
-
2474
Card::Codename[type_id.to_i]
-
end
-
-
1
def type_name
-
10940
type_card.try :name
-
end
-
-
1
alias_method :type, :type_name
-
-
1
def type_name_or_default
-
4
type_card.try(:name) || Card.quick_fetch(Card.default_type_id).name
-
end
-
-
1
def type_cardname
-
type_card.try :name
-
end
-
-
1
def type= type_name
-
self.type_id = Card.fetch_id type_name
-
end
-
-
1
def type_id= card_or_id
-
5292
write_card_or_id :type_id, card_or_id
-
end
-
-
1
def type_id_from_template
-
1303
return unless name && (t = template)
-
1303
reset_patterns # still necessary even with new template handling?
-
1303
self.type_id = t.type_id
-
end
-
-
1
event :validate_type_change, :validate, on: :update, changed: :type_id do
-
1
if (c = dup) && c.action == :create && !c.valid?
-
errors.add :type, tr(
-
:error_cant_change_errors,
-
name: name, type_id: type_id,
-
error_messages: c.errors.full_messages
-
)
-
end
-
end
-
-
1
event :validate_type, :validate, changed: :type_id, on: :save do
-
136
errors.add :type, tr(:error_no_such_type) unless type_name
-
-
136
if (rt = structure) && rt.assigns_type? && type_id != rt.type_id
-
errors.add :type, tr(:error_hard_templated, name: name, type_name: rt.type_name)
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/type.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (UpdateReadRules)
-
#
-
1
module UpdateReadRules;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/update_read_rules.rb"; end
-
-
# FIXME: the following don't really belong here, but they have to come after
-
# the reference stuff. we need to organize a bit!
-
-
1
event :update_rule_cache, :finalize, when: :is_rule? do
-
39
Card::Rule.clear_rule_cache
-
end
-
-
1
event :expire_related, :finalize do
-
216
reset_patterns
-
230
structuree_names.each { |name| Director.expirees << name } if is_structure?
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/update_read_rules.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Utils)
-
#
-
1
module Utils;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/utils.rb"; end
-
1
module ClassMethods
-
1
def merge_list attribs, opts={}
-
unmerged = []
-
attribs.each do |row|
-
if merge row["name"], row, opts
-
Rails.logger.info "merged #{row['name']}"
-
else
-
unmerged.push row
-
end
-
end
-
-
if unmerged.empty?
-
Rails.logger.info "successfully merged all!"
-
else
-
unmerged_json = JSON.pretty_generate unmerged
-
report_unmerged_json unmerged_json, opts[:output_file]
-
end
-
unmerged
-
end
-
-
1
def report_unmerged_json unmerged_json, output_file
-
if output_file
-
::File.open output_file, "w" do |f|
-
f.write unmerged_json
-
end
-
else
-
Rails.logger.info "failed to merge:\n\n#{unmerged_json}"
-
end
-
end
-
-
1
def merge name, attribs={}, opts={}
-
# puts "merging #{name}"
-
card = fetch name, new: {}
-
return unless mergeable? card, opts[:pristine]
-
-
resolve_file_attributes! attribs
-
card.safe_update! attribs
-
end
-
-
1
private
-
-
1
def resolve_file_attributes! attribs
-
%i[image file].each do |attach|
-
next unless attribs[attach] && attribs[attach].is_a?(String)
-
-
attribs[attach] = ::File.open(attribs[attach])
-
end
-
end
-
-
1
def mergeable? card, pristine_only
-
return true unless pristine_only
-
-
!card.pristine?
-
end
-
end
-
-
# separate name and other attributes
-
1
def safe_update! attribs
-
separate_name_update! attribs.delete("name") unless new?
-
update! attribs if attribs.present?
-
end
-
-
1
def separate_name_update! new_name
-
return if new_name.to_s == name.to_s
-
-
update! name: new_name
-
end
-
-
# rubocop:disable Style/GlobalVars
-
1
def measure desc
-
$times ||= {}
-
res = nil
-
t = Benchmark.measure do
-
res = yield
-
end
-
$times[desc] = $times.key?(desc) ? t + $times[desc] : t
-
puts "#{desc}: #{t}".red
-
res
-
end
-
# rubocop:enable Style/GlobalVars
-
-
1
def mod_root modname
-
if (spec = Gem::Specification.find_by_name "card-mod-#{modname}")
-
spec.full_gem_path
-
else
-
"#{Cardio.gem_root}/mod/#{modname}"
-
end
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
delegate :measure, to: :card
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/utils.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (AllCss)
-
#
-
1
module AllCss;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/all/all_css.rb"; end
-
-
2
module CssFormat; module_parent.send :register_set_format, Card::Format::CssFormat, self; extend Card::Set::AbstractFormat
-
1
def default_nest_view
-
:raw
-
end
-
-
1
def show view, args
-
view ||= :content
-
render! view, args
-
end
-
-
1
view :titled do
-
major_comment(%( Style Card: \\"#{card.name}\\" )) + _render_core
-
end
-
-
1
view :content do
-
_render_core
-
end
-
-
1
view :unknown do
-
major_comment "MISSING Style Card: #{card.name}"
-
end
-
-
1
view :import do
-
_render_core
-
end
-
-
1
view :url, perms: :none do
-
path mark: card.name, format: :css
-
end
-
-
1
def major_comment comment, char="-"
-
edge = %(/* #{char * (comment.length + 4)} */)
-
main = %(/* #{char} #{comment} #{char} */)
-
"#{edge}\n#{main}\n#{edge}\n\n"
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/all/all_css.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (AllCsv)
-
#
-
1
module AllCsv;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/all/all_csv.rb"; end
-
1
require "csv"
-
-
2
module CsvFormat; module_parent.send :register_set_format, Card::Format::CsvFormat, self; extend Card::Set::AbstractFormat
-
1
def default_nest_view
-
:core
-
end
-
-
1
def default_item_view
-
depth.zero? ? :csv_row : :name
-
end
-
-
1
view :core do
-
if (item_view_options[:view] == :name_with_fields) && focal?
-
title_row("item name") + name_with_field_rows
-
else
-
super()
-
end
-
end
-
-
1
view :csv_row do
-
array = _render_raw.scan(/\{\{[^\}]*\}\}/).map do |inc|
-
process_content(inc).strip
-
end
-
-
CSV.generate_line(array).strip
-
# strip is because search already joins with newlines
-
end
-
-
1
view :unknown do
-
""
-
end
-
-
1
view :name_with_fields do
-
CSV.generate_line name_with_fields_row
-
end
-
-
1
def name_with_fields_row
-
nested_field_names.each_with_object([card.name]) do |field_name, row|
-
row << nest(field_name)
-
end
-
end
-
-
1
def name_with_field_rows
-
return [] unless row_card_names.present?
-
-
row_card_names.map do |item_name|
-
CSV.generate_line row_from_field_names(item_name, columns)
-
end.join
-
end
-
-
1
def row_card_names
-
@row_cards ||= card.item_names
-
end
-
-
1
def columns
-
csv_structure_card.format.nested_field_names.map(&:tag)
-
end
-
-
1
def csv_structure_card
-
card.rule_card(:csv_structure) || Card.fetch(row_card_names.first)
-
end
-
-
1
def row_from_field_names parent_name, field_names, view=:core
-
field_names.each_with_object([parent_name]) do |field, row|
-
row << nest([parent_name, field], view: view)
-
end
-
end
-
-
1
def title_row extra_titles=nil
-
titles = column_titles extra_titles
-
return "" unless titles.present?
-
CSV.generate_line titles.map(&:upcase)
-
end
-
-
1
def column_titles extra_titles=nil
-
res = Array extra_titles
-
card1 = Card.fetch card.item_names(limit: 1).first
-
card1.nest_chunks.each do |chunk|
-
res << column_title(chunk.options)
-
end
-
res.compact
-
end
-
-
1
def column_title opts
-
if opts[:title]
-
opts[:title]
-
elsif %w[name link].member? opts[:view]
-
opts[:view]
-
else
-
opts[:nest_name].to_name.tag
-
end
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/all/all_csv.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (AllJs)
-
#
-
1
module AllJs;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/all/all_js.rb"; end
-
-
2
module JsFormat; module_parent.send :register_set_format, Card::Format::JsFormat, self; extend Card::Set::AbstractFormat
-
1
def default_item_view
-
:core
-
end
-
-
1
view :source do
-
path format: :js
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/all/all_js.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Base)
-
#
-
1
module Base;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/all/base.rb"; end
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
def show view, args
-
30
view ||= :core
-
30
render! view, args.merge(main_nest_options)
-
end
-
-
# NAME VIEWS
-
-
1
view :name, compact: true, perms: :none do
-
72
name_variant safe_name
-
end
-
-
1
def safe_name
-
324
card&.name
-
end
-
-
1
def name_variant name
-
1755
voo.variant ? name.to_name.vary(voo.variant) : name
-
end
-
-
1
view(:key, compact: true, perms: :none) { card.key }
-
37
view(:linkname, compact: true, perms: :none) { card.name.url_key }
-
37
view(:url, compact: true, perms: :none) { card_url _render_linkname }
-
-
1
view :url_link, compact: true, perms: :none do
-
link_to_resource card_url(_render_linkname)
-
end
-
-
1
view :link, compact: true, perms: :none do
-
147
link_view
-
end
-
-
1
view :nav_link, compact: true, perms: :none do
-
link_view class: "nav-link"
-
end
-
-
1
def link_view opts={}
-
371
opts[:known] = card.known?
-
371
specify_type_in_link! opts
-
371
link_to_card card.name, _render_title, opts
-
end
-
-
1
def specify_type_in_link! opts
-
371
return if opts[:known] || !voo.type
-
opts[:path] = { card: { type: voo.type } }
-
end
-
-
1
view(:codename, compact: true) { card.codename.to_s }
-
1
view(:id, compact: true) { card.id }
-
1
view(:type, compact: true) { card.type_name }
-
-
# DATE VIEWS
-
-
1
view(:created_at, compact: true) { date_view card.created_at }
-
1
view(:updated_at, compact: true) { date_view card.updated_at }
-
1
view(:acted_at, compact: true) { date_view card.acted_at }
-
-
1
def date_view date
-
if voo.variant
-
date.strftime voo.variant
-
else
-
time_ago_in_words date
-
end
-
end
-
-
# CONTENT VIEWS
-
-
1
view :raw do
-
2385
structure_card&.content || _render_blank
-
end
-
-
1
def structure_card
-
2385
return nil if voo.structure == true
-
2385
voo.structure ? Card[voo.structure] : card
-
end
-
-
1
view :core, compact: true do
-
1624
process_content _render_raw
-
end
-
-
1
view :content do
-
_render_core
-
end
-
-
1
view :open_content do
-
_render_core
-
end
-
-
1
view :one_line_content, compact: true do
-
with_nest_mode :compact do
-
Card::Content.smart_truncate _render_core
-
end
-
end
-
-
1
view :labeled_content, unknown: :mini_unknown do
-
6
render_core
-
end
-
-
1
view :titled_content, unknown: :blank do
-
73
render_core
-
end
-
-
1
view :blank, compact: true, perms: :none do
-
527
""
-
end
-
-
# note: content and open_content may look like they should be aliased to
-
# core, but it's important that they render core explicitly so that core view
-
# overrides work. the titled and labeled views below, however, are not
-
# intended for frequent override, so this shortcut is fine.
-
-
# NAME + CONTENT VIEWS
-
-
1
view :titled do
-
"#{card.name}\n\n#{_render_core}"
-
end
-
1
view :open, :titled
-
-
1
view :labeled do
-
"#{card.name}: #{_render_labeled_content}"
-
end
-
1
view :closed, :labeled
-
-
# SPECIAL VIEWS
-
-
1
view :array, cache: :never do
-
card.item_cards(limit: 0).map do |item_card|
-
subformat(item_card)._render_core
-
end.inspect
-
end
-
-
# DEPRECATED
-
1
view :naked do
-
Rails.logger.info "DEPRECATED: naked view (used with #{card.name} card)"
-
render_core
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/all/base.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (File)
-
#
-
1
module File;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/all/file.rb"; end
-
2
module FileFormat; module_parent.send :register_set_format, Card::Format::FileFormat, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
"File rendering of this card not yet supported"
-
end
-
-
1
view :style do
-
nil
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/all/file.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Head)
-
#
-
1
module Head;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/all/head.rb"; end
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
view :page_title, unknown: true, perms: :none do
-
224
title_parts = [Card::Rule.global_setting(:title)]
-
224
title_parts.unshift safe_name if card.name.present?
-
224
title_parts.join " - "
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
# add tuples containing a
-
# - the codename of a card with javascript config (usually in json format)
-
# - the name of a javascript method that handles the config
-
1
basket :mod_js_config
-
-
1
view :head, unknown: true, perms: :none do
-
2688
views_in_head.map { |viewname| render viewname }.flatten.compact.join "\n"
-
end
-
-
1
def views_in_head
-
224
%i[meta_tags page_title_tag favicon_tag head_stylesheet
-
decko_script_variables head_javascript html5shiv_tag
-
script_config_and_initiation
-
universal_edit_button rss_links]
-
end
-
-
# FIXME: tags not working with `template: :haml`
-
1
view :meta_tags, unknown: true, perms: :none do
-
224
haml :meta_tags
-
end
-
-
1
view :html5shiv_tag, unknown: true, perms: :none do
-
224
nest :script_html5shiv_printshiv, view: :script_tag
-
end
-
-
1
view :page_title_tag, unknown: true, perms: :none do
-
448
content_tag(:title) { render :page_title }
-
end
-
-
1
view :favicon_tag, unknown: true, perms: :none do
-
224
nest :favicon, view: :link_tag
-
end
-
-
1
view :universal_edit_button, unknown: true, denial: :blank, perms: :update do
-
213
return if card.new?
-
160
tag "link", rel: "alternate", type: "application/x-wiki",
-
title: "Edit this page!", href: path(view: :edit)
-
end
-
-
# these should render a view of the rule card
-
# it would then be safe to cache if combined with param handling
-
# (but note that machine clearing would need to reset card cache...)
-
1
view :head_stylesheet, unknown: true, cache: :never, perms: :none do
-
224
return unless (href = head_stylesheet_path)
-
224
tag "link", href: href, media: "all", rel: "stylesheet", type: "text/css"
-
end
-
-
1
view :head_javascript, unknown: true, cache: :never, perms: :none do
-
224
Array.wrap(head_javascript_paths).map do |path|
-
224
javascript_include_tag path
-
end
-
end
-
-
1
view :decko_script_variables, unknown: true, cache: :never, perms: :none do
-
224
string = ""
-
224
decko_script_variables.each do |k, v|
-
896
string += "#{k}=#{script_variable_to_js v};\n"
-
end
-
448
javascript_tag { string }
-
end
-
-
1
def decko_script_variables
-
{
-
224
"window.decko": { rootUrl: card_url("") },
-
"decko.doubleClick": Card.config.double_click,
-
"decko.cssPath": head_stylesheet_path,
-
224
"decko.currentUserId": (Auth.current_id if Auth.signed_in?)
-
-
}
-
end
-
-
1
def script_variable_to_js value
-
1120
if value.is_a? Hash
-
224
string = "{"
-
448
value.each { |k, v| string += "#{k}:#{script_variable_to_js v}" }
-
224
string + "}"
-
else
-
896
"'#{value}'"
-
end
-
end
-
-
1
def param_or_rule_card setting
-
672
if params[setting]
-
Card[params[setting]]
-
else
-
672
root.card.rule_card setting
-
end
-
end
-
-
1
def debug_or_machine_path setting, &block
-
672
return unless (asset_card = param_or_rule_card setting)
-
672
debug_path(setting, asset_card, &block) || asset_card.machine_output_url
-
end
-
-
1
def debug_path setting, asset_card
-
672
return unless params[:debug] == setting.to_s
-
yield asset_card
-
end
-
-
1
def head_stylesheet_path
-
448
debug_or_machine_path :style do |style_card|
-
path mark: style_card.name, item: :import, format: :css
-
end
-
end
-
-
1
def head_javascript_paths
-
224
debug_or_machine_path :script do |script_card|
-
script_card.item_cards.map do |script|
-
script.format(:js).render :source
-
end
-
end
-
end
-
-
1
view :script_config_and_initiation, unknown: true, perms: :none do
-
224
javascript_tag do
-
224
(mod_js_configs << trigger_slot_ready).join "\n\n"
-
end
-
end
-
-
1
def mod_js_configs
-
224
mod_js_config.map do |codename, js_decko_function|
-
896
config_json = escape_javascript Card::Rule.global_setting(codename)
-
896
"decko.#{js_decko_function}('#{config_json}')"
-
end
-
end
-
-
1
def trigger_slot_ready
-
224
"$('document').ready(function() { $('.card-slot').trigger('slotReady'); })"
-
end
-
-
# TODO: move to rss mod
-
1
view :rss_links, unknown: true, perms: :none do
-
224
render :rss_link_tag if rss_link?
-
end
-
-
1
def rss_link?
-
224
Card.config.rss_enabled && respond_to?(:rss_link_tag)
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/all/head.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Json)
-
#
-
1
module Json;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/all/json.rb"; end
-
2
module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
-
# because card.item_cards returns "[[#{self}]]"
-
1
def item_cards
-
nested_cards
-
end
-
-
1
def default_nest_view
-
:atom
-
end
-
-
1
def default_item_view
-
params[:item] || :name
-
end
-
-
1
def max_depth
-
params[:max_depth].present? ? params[:max_depth].to_i : 1
-
end
-
-
# TODO: support layouts in json
-
# eg layout=stamp gives you the metadata currently in "page" view
-
# and layout=none gives you ONLY the requested view (default atom)
-
1
def show view, args
-
11
view ||= :molecule
-
11
raw = render! view, args
-
11
raw.is_a?(String) ? raw : stringify(raw)
-
end
-
-
1
def stringify raw
-
11
method = params[:compress] ? :generate : :pretty_generate
-
11
JSON.send method, raw
-
end
-
-
1
view :status, unknown: true, perms: :none do
-
8
status = card.state
-
8
hash = { key: card.key,
-
url_key: card.name.url_key,
-
status: status }
-
8
hash[:id] = card.id if status == :real
-
8
hash
-
end
-
-
# NOCACHE because of timestamp
-
1
view :page, cache: :never do
-
{ url: request_url,
-
timestamp: Time.now.to_s,
-
card: _render_atom }
-
end
-
-
1
def request_url
-
req = controller.request
-
req ? req.original_url : path
-
end
-
-
1
view :core, unknown: true do
-
card.known? ? render_content : nil
-
end
-
-
1
view :content do
-
card.content
-
end
-
-
1
view :nucleus do
-
nucleus
-
end
-
-
# TODO: add simple values for fields
-
1
view :atom, unknown: true do
-
atom
-
end
-
-
1
view :molecule do
-
molecule
-
end
-
-
# NOCACHE because sometimes item_cards is dynamic.
-
# could be safely cached for non-dynamic lists
-
1
view :items, cache: :never do
-
listing item_cards, view: :atom
-
end
-
-
1
view :links do
-
card.link_chunks.map do |chunk|
-
if chunk.referee_name
-
path mark: chunk.referee_name, format: :json
-
else
-
link_to_resource chunk.link_target
-
end
-
end
-
end
-
-
1
view :ancestors do
-
card.name.ancestors.map do |name|
-
nest name, view: :nucleus
-
end
-
end
-
-
# minimum needed to re-fetch card
-
1
view :cast do
-
card.cast
-
end
-
-
## DEPRECATED
-
1
view :marks do
-
{
-
id: card.id,
-
name: card.name,
-
key: card.key,
-
url: path
-
}
-
end
-
-
1
view :essentials do
-
if voo.show? :marks
-
render_marks.merge(essentials)
-
else
-
essentials
-
end
-
end
-
-
1
def essentials
-
return {} if card.structure
-
{ content: card.db_content }
-
end
-
-
# NOTE: moving these to methods prevents potential caching problems, because other
-
# views manipulate their hashes.
-
#
-
1
def nucleus
-
h = { id: card.id,
-
name: card.name,
-
type: card.type_name,
-
url: path(format: :json) }
-
h[:codename] = card.codename if card.codename
-
h
-
end
-
-
1
def atom
-
h = nucleus
-
h[:content] = render_content if card.known? && !card.structure
-
h
-
end
-
-
1
def molecule
-
atom.merge items: _render_items,
-
links: _render_links,
-
ancestors: _render_ancestors,
-
html_url: path,
-
type: nest(card.type_card, view: :nucleus)
-
end
-
end
-
-
# TODO: perhaps this should be in a general "data" module.
-
1
def cast
-
real? ? { id: id } : { name: name, type_id: type_id, content: db_content }
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/all/json.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Rss)
-
#
-
1
module Rss;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/all/rss.rb"; end
-
2
module RssFormat; module_parent.send :register_set_format, Card::Format::RssFormat, self; extend Card::Set::AbstractFormat
-
1
attr_accessor :xml
-
-
1
def initialize card, args
-
super
-
@xml = @parent ? @parent.xml : ::Builder::XmlMarkup.new
-
end
-
-
1
def show view, args
-
view ||= :feed
-
render! view, args
-
end
-
-
# FIXME: integrate this with common XML features when it is added
-
1
view :feed, cache: :never do
-
return "RSS feeds disabled" unless Cardio.config.rss_enabled
-
begin
-
@xml.instruct! :xml, version: "1.0", standalone: "yes"
-
@xml.rss version: "2.0",
-
"xmlns:content" => "http://purl.org/rss/1.0/modules/content/" do
-
@xml.channel do
-
@xml.title render_feed_title
-
@xml.description render_feed_description
-
@xml.link render_url
-
render_feed_body
-
end
-
end
-
rescue => e
-
@xml.error "\n\nERROR rendering RSS: #{e.inspect}\n\n #{e.backtrace}"
-
end
-
end
-
-
1
def raw_feed_items
-
[card]
-
end
-
-
1
view :feed_body, cache: :never do
-
raw_feed_items.each do |item|
-
@xml.item do
-
subformat(item).render! :feed_item
-
end
-
end
-
end
-
-
1
view :feed_title do
-
Card::Rule.global_setting(:title) + " : " + card.name.gsub(/^\*/, "")
-
end
-
-
1
view :feed_item do
-
@xml.title card.name
-
add_name_context
-
@xml.description render_feed_item_description
-
@xml.pubDate pub_date
-
@xml.link render_url
-
@xml.guid render_url
-
end
-
-
1
def pub_date
-
(card.updated_at || Time.zone.now).to_s(:rfc822)
-
# updated_at fails on virtual
-
# cards, because not all to_s's take args (just actual dates)
-
end
-
-
1
view :feed_item_description do
-
render_open_content
-
end
-
-
1
view(:feed_description) { "" }
-
1
view(:comment_box) { "" }
-
1
view(:menu) { "" }
-
-
1
view :open, :titled, mod: All::Base::Format
-
1
view :content, :core, mod: All::Base::Format
-
1
view :open_content, :core, mod: All::Base::Format
-
1
view :closed, :link, mod: All::Base::Format
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/all/rss.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Text)
-
#
-
1
module Text;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/all/text.rb"; end
-
-
2
module TextFormat; module_parent.send :register_set_format, Card::Format::TextFormat, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
48
HTMLEntities.new.decode strip_tags(super()).to_s
-
# need this string method to get out of html_safe mode
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/all/text.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Head"
-
#
-
1
module Head;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/self/head.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
# when *head is rendered in the main body of a page, we escape the HTML
-
# otherwise (most typically in the head tag, of course), we render the
-
# HTML unescaped
-
1
view :core, cache: :never do
-
224
escape_in_main do
-
224
nest root.card, view: :head
-
# note that the head tag for each card is different
-
# (different title, different style rules, etc)
-
# so we don't cache the core of *head, but we _do_ cache some
-
# views within each head (see all/head.rb)
-
end
-
end
-
-
1
view :input do
-
"Content can't be edited."
-
end
-
-
1
def escape_in_main
-
224
main? ? (h yield) : yield
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/self/head.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Html" cards
-
#
-
1
module Html;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/type/html.rb"; end
-
1
def clean_html?
-
3
false
-
end
-
-
1
def diff_args
-
{ diff_format: :raw }
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
view :one_line_content do
-
raw_one_line_content
-
end
-
-
1
def chunk_list
-
1158
:references
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def input_type
-
3
:ace_editor
-
end
-
-
1
view :one_line_content, wrap: {} do
-
raw_one_line_content
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/type/html.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Json" cards
-
#
-
# include_set Abstract::Pointer
-
1
module Json;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/type/json.rb"; end
-
-
1
event :validate_json, :validate, on: :save, changed: :content do
-
20
check_json_syntax if content.present?
-
end
-
-
1
def check_json_syntax
-
20
parse_content
-
rescue JSON::ParserError => e
-
errors.add tr(:invalid_json), e.message.sub(/^\d+: /, "").to_s
-
end
-
-
1
def parse_content
-
20
JSON.parse content
-
end
-
-
1
def item_names _args={}
-
parse_content.keys.map(&:to_name)
-
end
-
-
1
def item_values
-
parse_content.values
-
end
-
-
1
def item_value name
-
parse_content[name]
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
process_content ::CodeRay.scan(_render_raw, :json).div
-
end
-
-
1
def input_type
-
:ace_editor
-
end
-
-
1
def ace_mode
-
:json
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/type/json.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "PlainText" cards
-
#
-
1
module PlainText;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/type/plain_text.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def input_type
-
1
:text_area
-
end
-
-
1
view :core do
-
1
process_content CGI.escapeHTML(_render_raw)
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/type/plain_text.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (PagingParams)
-
#
-
1
module PagingParams;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/00_paging_params.rb"; end
-
1
MAX_ANONYMOUS_SEARCH_PARAM = 1000
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
def limit_param
-
59
@limit ||= contextual_param(:limit) || default_limit
-
end
-
-
1
def offset_param
-
59
@offset ||= contextual_param(:offset) || 0
-
end
-
-
1
def contextual_param param
-
54
env_search_param(param) || voo_search_param(param)
-
end
-
-
1
def env_search_param param
-
54
return unless focal? && Env.params[param].present?
-
-
2
legal_search_param! param, Env.params[param].to_i
-
end
-
-
1
def legal_search_param! param, val
-
2
return val if Card::Auth.signed_in? || val <= MAX_ANONYMOUS_SEARCH_PARAM
-
-
raise Card::Error::PermissionDenied, "#{param} parameter exceeds maximum"
-
end
-
-
1
def voo_search_param param
-
52
voo&.cql&.dig param
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/00_paging_params.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (Paging)
-
#
-
1
module Paging;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/01_paging.rb"; end
-
1
include_set Abstract::PagingParams
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
def limit
-
48
limit_param
-
end
-
-
1
def offset
-
48
offset_param
-
end
-
-
1
def search_with_params
-
16
card.item_names
-
end
-
-
1
def count_with_params
-
card.item_names.count
-
end
-
-
1
def total_pages
-
3
return 1 if limit.zero?
-
3
((count_with_params - 1) / limit).to_i
-
end
-
-
1
def current_page
-
3
(offset / limit).to_i
-
end
-
-
# for override
-
1
def extra_paging_path_args
-
16
{}
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
PAGE_LI_CLASS = { ellipses: "disabled", current: "active" }.freeze
-
-
1
def with_paging path_args={}
-
20
with_paging_path_args path_args do
-
20
output [yield(@paging_path_args), _render_paging]
-
end
-
end
-
-
1
view :paging, cache: :never do
-
20
return "" unless paging_needed?
-
3
<<-HTML
-
<nav>
-
<ul class="pagination paging">
-
#{paging_links.join}
-
</ul>
-
</nav>
-
HTML
-
end
-
-
1
def paging_links
-
3
PagingLinks.new(total_pages, current_page)
-
.build do |text, page, status, options|
-
19
page_link_li text, page, status, options
-
end
-
end
-
-
# First page is 0 (not 1)
-
1
def page_link_li text, page, status, options={}
-
19
wrap_with :li, class: page_link_li_class(status) do
-
19
page_link text, page, options
-
end
-
end
-
-
1
def page_link_li_class status
-
19
["page-item", PAGE_LI_CLASS[status]].compact.join " "
-
end
-
-
1
def page_link text, page, options
-
19
return content_tag(:div, text.html_safe, class: "page-link") unless page
-
-
15
options.merge! class: "card-paging-link slotter page-link",
-
remote: true,
-
path: page_link_path_args(page)
-
15
link_to raw(text), options
-
end
-
-
1
def with_paging_path_args args
-
20
tmp = @paging_path_args
-
20
@paging_path_args = paging_path_args args
-
20
yield
-
ensure
-
20
@paging_path_args = tmp
-
end
-
-
1
def paging_path_args local_args={}
-
35
@paging_path_args ||= {}
-
35
@paging_path_args.reverse_merge!(limit: limit, offset: offset)
-
35
@paging_path_args.merge! extra_paging_path_args
-
35
@paging_path_args.merge local_args
-
end
-
-
1
def page_link_path_args page
-
15
paging_path_args.merge offset: page * limit
-
end
-
-
1
def paging_needed?
-
20
return false if limit < 1
-
20
return false if fewer_results_than_limit? # avoid extra count search
-
-
# count search result instead
-
3
limit < count_with_params
-
end
-
-
# clear we don't need paging even before running count query
-
1
def fewer_results_than_limit?
-
20
return false unless offset.zero?
-
-
19
limit > offset + search_with_params.length
-
end
-
end
-
-
2
module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
-
1
def page_link_path_args page
-
{
-
limit: limit,
-
offset: page * limit,
-
item: default_item_view, # hack. need standard voo handling
-
format: :json
-
}.merge extra_paging_path_args
-
end
-
-
1
view :paging_urls, cache: :never do
-
return {} unless total_pages > 1
-
-
{ paging: paging_urls_hash }
-
end
-
-
1
def paging_urls_hash
-
hash = {}
-
PagingLinks.new(total_pages, current_page)
-
.build do |_text, page, status, _options|
-
add_paging_url hash, page, status
-
end
-
hash
-
end
-
-
1
def add_paging_url hash, page, status
-
return unless page && status.in?(%i[next previous])
-
-
hash[status] = path page_link_path_args(page)
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/01_paging.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (Paging)
-
#
-
#! no set module
-
-
1
module Paging;
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/01_paging/paging_links.rb"; end
-
# render paging links
-
1
class PagingLinks
-
1
def initialize total_pages, current_page
-
3
@total = total_pages
-
3
@current = current_page
-
end
-
-
# @param window [integer] number of page links shown left and right
-
# of the current page
-
# @example: current page = 5, window = 2
-
# |<<|1|...|3|4|[5]|6|7|...|10|>>|
-
# @yield [text, page, status, options] block to build single paging link
-
# @yieldparam status [Symbol] :active (for current page) or :disabled
-
# @yieldparam page [Integer] page number, first page is 0
-
# @return [Array<String>]
-
1
def build window=2, &block
-
3
@render_item = block
-
3
links window
-
end
-
-
1
private
-
-
1
def links window
-
3
@window_start = [@current - window, 0].max
-
3
@window_end = [@current + window, @total].min
-
3
left_part + window_part + right_part
-
end
-
-
# the links around the current page
-
1
def window_part
-
3
(@window_start..@window_end).map do |page|
-
9
direct_page_link page
-
end.compact
-
end
-
-
1
def left_part
-
[
-
3
previous_page_link,
-
3
(direct_page_link 0 if @window_start > 0),
-
3
(ellipse if @window_start > 1)
-
].compact
-
end
-
-
1
def right_part
-
[
-
3
(ellipse if @total > @window_end + 1),
-
3
(direct_page_link @total if @total > @window_end),
-
next_page_link
-
].compact
-
end
-
-
1
def previous_page_link
-
3
paging_item '<span aria-hidden="true">«</span>', previous_page,
-
"aria-label" => "Previous", status: :previous
-
end
-
-
1
def next_page_link
-
3
paging_item '<span aria-hidden="true">»</span>', next_page,
-
"aria-label" => "Next", status: :next
-
end
-
-
1
def direct_page_link page
-
11
return unless page >= 0 && page <= @total
-
11
paging_item page + 1, page
-
end
-
-
1
def ellipse
-
2
paging_item "<span>...</span>", nil, status: :ellipses
-
end
-
-
1
def paging_item text, page, options={}
-
status =
-
19
if page == @current
-
3
:current
-
else
-
16
options.delete :status
-
end
-
19
@render_item.call text, page, status, options
-
end
-
-
1
def previous_page
-
3
@current > 0 ? @current - 1 : false
-
end
-
-
1
def next_page
-
3
@current < @total ? @current + 1 : false
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/01_paging/paging_links.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (Items)
-
#
-
# ~~~~~~~~~~~~ READING ITEMS ~~~~~~~~~~~~
-
-
1
module Items;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_items.rb"; end
-
# While each of the three main methods for returning lists of items can handle arguments,
-
# they are most commonly used without them.
-
-
# @return [Array] list of Card::Name objects
-
# @param args [Hash]
-
# @option args [String] :content override card content
-
# @option args [String, Card::Name, Symbol] :context name in whose context relative items
-
# will be interpreted. For example. +A in context of B is interpreted as B+A
-
# context defaults to pointer card's name. If value is `:raw`, then name is not
-
# contextualized
-
# @option args [String, Integer] :limit max number of cards to return
-
# @option args [String, Integer] :offset begin after the offset-th item
-
1
def item_names args={}
-
1774
context = args[:context]
-
1774
item_strings(args).map do |item|
-
4681
clean_item_name item, context
-
end.compact
-
end
-
-
1
def first_name args={}
-
item_names(args).first
-
end
-
-
1
def first_card args={}
-
return unless (name = first_name)
-
fetch_item_card name, args
-
end
-
-
1
def first_code
-
first_card&.codename
-
end
-
-
# @return [Array] list of integers (card ids of items)
-
# @param args [Hash] see #item_names
-
1
def item_ids args={}
-
352
item_names(args).map { |name| Card.fetch_id name }.compact
-
end
-
-
# @return [Array] list of Card objects
-
# @param args [Hash] see #item_names for additional options
-
# @option args [String] :complete keyword to use in searching among items
-
# @option args [True/False] :known_only if true, return only known cards
-
# @option args [String] :type name of type to be used for unknown cards
-
1
def item_cards args={}
-
321
return item_cards_search(args) if args[:complete]
-
321
return known_item_cards(args) if args[:known_only]
-
321
all_item_cards args
-
end
-
-
# #item_name, #item_id, and #item_card each return a single item, rather than an array.
-
1
%i[name id card].each do |obj|
-
3
define_method "item_#{obj}" do |args={}|
-
259
send("item_#{obj}s", args.merge(limit: 1)).first
-
end
-
end
-
-
# for override, eg by json
-
1
def item_value item_name
-
item_name
-
end
-
-
# ~~~~~~~~~~~~ ALTERING ITEMS ~~~~~~~~~~~~
-
-
# set card content based on array and save card
-
# @param array [Array] list of strings/names (Cardish)
-
1
def items= array
-
items_to_content array
-
save!
-
end
-
-
# append item to list (does not save)
-
# @param cardish [Cardish]
-
1
def << cardish
-
add_item cardish
-
end
-
-
# append item to list (does not save)
-
# @param cardish [String, Card::Name] item name
-
# @param allow_duplicates [True/False] permit duplicate items (default is False)
-
1
def add_item cardish, allow_duplicates=false
-
return if !allow_duplicates && include_item?(cardish)
-
-
items = item_strings << cardish
-
items_to_content items
-
end
-
-
# append item to list and save card
-
# @param name [String, Card::Name] item name
-
1
def add_item! name
-
add_item(name) && save!
-
end
-
-
# remove item from list
-
# @param cardish [String, Card::Name] item to drop
-
1
def drop_item cardish
-
drop_item_name = Card::Name[cardish]
-
items_to_content(item_names.reject { |item_name| item_name == drop_item_name })
-
end
-
-
# remove item from list and save card
-
# @param cardish [String, Card::Name] item to drop
-
1
def drop_item! cardish
-
drop_item cardish
-
save!
-
end
-
-
# insert item into list at specified location
-
# @param index [Integer] Array index in which to insert item (0 is first)
-
# @param name [String, Card::Name] item name
-
1
def insert_item index, name
-
37
new_names = item_names
-
37
new_names.delete name
-
37
new_names.insert index, name
-
37
items_to_content new_names
-
end
-
-
# insert item into list at specified location and save
-
# @param index [Integer] Array index in which to insert item (0 is first)
-
# @param name [String, Card::Name] item name
-
1
def insert_item! index, name
-
insert_item index, name
-
save!
-
end
-
-
# ~~~~~~~~~~~~ READING ITEM HELPERS ~~~~~~~~~~~~
-
-
# Warning: the following methods, while available for use, may be subject to change
-
-
# #item_cards helpers
-
-
1
def item_cards_search query
-
Card::Query.run query.reverse_merge(referred_to_by: name, limit: 0)
-
end
-
-
1
def known_item_cards args={}
-
item_names(args).map { |name| Card.fetch name }.compact
-
end
-
-
1
def all_item_cards args={}
-
321
names = args[:item_names] || item_names(args)
-
647
names.map { |name| fetch_item_card name, args }
-
end
-
-
# TODO: support type_code and type_id. (currently type)
-
# uses name, because its most common use is from CQL
-
1
def item_type
-
345
opt = options_rule_card
-
# FIXME: need better recursion prevention
-
345
return if !opt || opt == self
-
328
opt.item_type
-
end
-
-
1
def item_strings args={}
-
1784
items = raw_item_strings(args[:content] || content)
-
1784
return items unless args.present?
-
-
381
filtered_items items, args.slice(:limit, :offset)
-
end
-
-
1
def raw_item_strings content
-
6484
content.to_s.split(/\n+/).map { |i| strip_item i }
-
end
-
-
1
private
-
-
1
def filtered_items items, limit: 0, offset: 0
-
381
limit = limit.to_i
-
381
offset = offset.to_i
-
381
return items unless limit.positive? || offset.positive?
-
-
271
items[offset, (limit.zero? ? items.size : limit)] || []
-
end
-
-
1
def fetch_item_card name, args={}
-
326
Card.fetch name, new: new_unknown_item_args(args)
-
end
-
-
1
def new_unknown_item_args args
-
326
itype = args[:type] || item_type
-
326
itype ? { type: itype } : {}
-
end
-
-
1
def clean_item_name item, context
-
4681
item = item.to_name
-
4681
return item if context == :raw
-
4681
context ||= context_card.name
-
4681
item.absolute_name context
-
rescue Card::Error::NotFound
-
# eg for invalid ids or codenames
-
# "Invalid Item: #{item}".to_name
-
nil
-
end
-
-
1
def strip_item item
-
4700
item.gsub(/\[\[|\]\]/, "").strip
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_items.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (Pointer)
-
#
-
1
module Pointer;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer.rb"; end
-
1
include_set Abstract::Paging
-
1
include_set Abstract::Items
-
-
1
def diff_args
-
{ diff_format: :pointer }
-
end
-
-
1
def count
-
5
item_strings.size
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Abstract; module Pointer;
-
# Set: Abstract (Pointer, Events)
-
#
-
1
module Events;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer/events.rb"; end
-
1
event :add_and_drop_items, :prepare_to_validate, on: :save do
-
70
adds = Env.params["add_item"]
-
70
drops = Env.params["drop_item"]
-
70
Array.wrap(adds).each { |i| add_item i } if adds
-
70
Array.wrap(drops).each { |i| drop_item i } if drops
-
end
-
-
1
event :insert_item_event, :prepare_to_validate, on: :save, when: :item_to_insert do
-
index = Env.params["item_index"] || 0
-
insert_item index.to_i, item_to_insert
-
end
-
-
1
def item_to_insert
-
70
Env.params["insert_item"]
-
end
-
-
# If a card's type and content are updated in the same action, the new module
-
# will override the old module's events and functions. But this event is only
-
# on pointers -- other type cards do not have this event,
-
# Therefore if something is changed from a pointer and its content is changed
-
# in the same action, this event will be run and will treat the content like
-
# it' still pointer content. The "when" clause helps with that (but is a hack)
-
1
event :standardize_items, :prepare_to_validate,
-
on: :save, changed: :content, when: :still_pointer? do
-
5
items_to_content item_strings
-
end
-
-
1
def still_pointer?
-
59
type_id == PointerID
-
# Card.new(type_id: type_id).is_a? Abstract::Pointer
-
end
-
-
1
def changed_item_names
-
dropped_item_names + added_item_names
-
end
-
-
1
def dropped_item_names
-
return item_names if trash
-
return [] unless (old_content = db_content_before_act)
-
-
old_items = item_names content: old_content
-
old_items - item_names
-
end
-
-
1
def added_item_names
-
return [] if trash
-
return item_names unless (old_content = db_content_before_act)
-
-
old_items = item_names content: old_content
-
item_names - old_items
-
end
-
-
# TODO: refactor. many of the above could be written more elegantly with improved
-
# handling of :content in item_names. If content is nil here, we would expect an
-
# empty set of cards, but in fact we get items based on self.content.
-
-
1
def changed_item_cards
-
dropped_item_cards + added_item_cards
-
end
-
-
1
def dropped_item_cards
-
return [] unless db_content_before_act
-
-
all_item_cards item_names: dropped_item_names
-
end
-
-
1
def added_item_cards
-
return item_cards unless db_content_before_act
-
-
all_item_cards item_names: added_item_names
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer/events.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Abstract; module Pointer;
-
# Set: Abstract (Pointer, HtmlViews)
-
#
-
1
module HtmlViews;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer/html_views.rb"; end
-
1
include_set Abstract::BsBadge
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :core, cache: :never do
-
16
standard_pointer_core
-
end
-
-
1
view :item_cores, cache: :never do
-
card.known_item_cards.map do |item|
-
nest item, view: :core
-
end.join "\n"
-
end
-
-
1
def standard_pointer_core
-
16
with_paging do |paging_args|
-
16
wrap_with :div, standard_pointer_items(paging_args), class: "pointer-list"
-
end
-
end
-
-
1
def standard_pointer_items paging_args
-
16
pointer_items(paging_args.extract!(:limit, :offset)).join(voo.separator || "\n")
-
end
-
-
1
view :one_line_content do
-
item_view = implicit_item_view
-
item_view = item_view == "name" ? "name" : "link"
-
wrap_with :div, class: "pointer-list" do
-
# limit to first 10 items to optimize
-
pointer_items(view: item_view, limit: 10, offset: 0).join ", "
-
end
-
end
-
-
1
def wrap_item rendered, item_view
-
19
%(<div class="pointer-item item-#{item_view}">#{rendered}</div>)
-
end
-
-
1
view :input do
-
194
_render_hidden_content_field + super()
-
end
-
-
1
def default_input_type
-
:list
-
end
-
-
1
view :list, cache: :never do
-
list_input
-
end
-
-
# view :nav_item do
-
# nav_dropdown
-
# end
-
-
1
def list_input args={}
-
items = items_for_input args[:item_list]
-
extra_class = "pointer-list-ul"
-
ul_classes = classy "pointer-list-editor", extra_class
-
haml :list_input, items: items, ul_classes: ul_classes,
-
options_card: options_card_name
-
end
-
-
1
%i[autocomplete checkbox radio select multiselect].each do |editor_view|
-
5
view(editor_view) { send "#{editor_view}_input" }
-
end
-
-
1
def autocomplete_input
-
items = items_for_input
-
haml :autocomplete_input, item: items.first, options_card: options_card_name
-
end
-
-
1
def checkbox_input
-
2
haml :checkbox_input, submit_on_change: @submit_on_change
-
end
-
-
1
def radio_input
-
1
haml :radio_input, submit_on_change: @submit_on_change
-
end
-
-
1
def select_input
-
options = { "-- Select --" => "" }.merge card.options_hash
-
select_tag("pointer_select-#{unique_id}",
-
options_for_select(options, card.item_name),
-
class: "pointer-select form-control")
-
end
-
-
1
def multiselect_input
-
1
select_tag "pointer_multiselect-#{unique_id}",
-
options_for_select(card.options_hash, card.item_names),
-
multiple: true, class: "pointer-multiselect form-control"
-
end
-
-
1
def add_item_modal_link
-
modal_link "Add Item",
-
size: :large,
-
class: "btn btn-sm btn-outline-secondary _add-item-link",
-
path: { view: :filter_items_modal,
-
item: implicit_item_view,
-
filter_card: filter_card.name,
-
slot_selector: filtered_list_slot_class,
-
item_selector: "_filtered-list-item",
-
slot: { hide: [:modal_footer] },
-
filter: { not_ids: not_ids_value } }
-
end
-
-
1
def not_ids_value
-
card.item_ids.map(&:to_s).join(",")
-
end
-
-
1
def add_item_overlay_link; end
-
-
1
def one_line_content
-
1
if count == 1
-
card.first_name
-
else
-
1
short_content
-
end
-
end
-
-
1
private
-
-
# currently only used by :list and :autocomplete. could be generalized?
-
1
def items_for_input items=nil
-
items ||= card.item_names context: :raw
-
items.empty? ? [""] : items
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer/html_views.rb ~~
-
# -*- encoding : utf-8 -*-
-
5
class Card; module Set; class Abstract; module Pointer;; module HtmlViews;
-
# Set: Abstract (Pointer, HtmlViews, Filter)
-
#
-
1
module Filter;
-
1
extend Card::Set
-
2
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer/html_views/filter.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :filtered_list, unknown: true do
-
filtered_list_input
-
end
-
-
1
view :filter_items_modal, unknown: true, wrap: :modal do
-
render_filter_items
-
end
-
-
1
view :filter_items, unknown: true, wrap: :slot, template: :haml
-
-
1
def filtered_list_input
-
with_nest_mode :normal do
-
class_up "card-slot", filtered_list_slot_class
-
with_class_up "card-slot", filtered_list_slot_class do
-
wrap do
-
haml :filtered_list_input
-
end
-
end
-
end
-
end
-
-
# NOCACHE because params alter view
-
1
view :add_selected_link, cache: :never, unknown: true do
-
link_to "Add Selected",
-
path: { filter_card: params[:filter_card] },
-
class: "_add-selected slotter _close-modal btn btn-primary disabled",
-
data: { "slot-selector": ".#{params[:slot_selector]}",
-
"item-selector": ".#{params[:item_selector]}",
-
remote: true }
-
end
-
-
1
def filtered_list_item item_card
-
nest_item item_card do |rendered, item_view|
-
wrap_item rendered, item_view
-
end
-
end
-
-
# for override
-
# @return [Card] search card on which filtering is based
-
1
def filter_card
-
filter_card_from_params || default_filter_card
-
end
-
-
1
def default_filter_card
-
fcard = card.options_rule_card || Card[:all]
-
return fcard if fcard.respond_to? :cql_hash
-
-
fcard.fetch :referred_to_by, new: {}
-
end
-
-
1
def filter_card_from_params
-
return unless params[:filter_card]
-
Card.fetch params[:filter_card], new: {}
-
end
-
-
# currently actually used as a class
-
# (because we don't have api to override slot's id)
-
1
def filtered_list_slot_class
-
@filtered_list_slot_class ||= "filtered-list-#{unique_id}"
-
end
-
end
-
end;end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer/html_views/filter.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Abstract; module Pointer;
-
# Set: Abstract (Pointer, OptionsApi)
-
#
-
# TODO: some of this should be moved to right/options!!
-
1
module OptionsApi;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer/options_api.rb"; end
-
# or to type/JSON?
-
-
1
def options_hash
-
4
json_options? ? options_card.parse_content : option_hash_from_names
-
end
-
-
1
def json_options?
-
8
options_card&.type_id == JsonID
-
end
-
-
1
def option_hash_from_names
-
4
option_names.each_with_object({}) do |name, hash|
-
56
hash[name] = name
-
end
-
end
-
-
1
def option_names
-
4
if (selected_options = item_names)
-
4
(standard_option_names + selected_options).uniq
-
else
-
standard_option_names
-
end
-
end
-
-
1
def option_cards
-
option_names.map do |name|
-
Card.fetch name, new: {}
-
end
-
end
-
-
1
def options_rule_card
-
54
rule_card :content_options
-
end
-
-
1
def standard_option_names
-
4
if json_options?
-
options_hash.values.map(&:to_name)
-
else
-
4
option_names_from_items
-
end
-
end
-
-
1
def option_names_from_items
-
4
o_card = options_card
-
4
limit = o_card.try(:default_limit).to_i
-
4
o_card.item_names context: name, limit: limit
-
end
-
-
1
def options_card
-
12
options_rule_card || Card[:all]
-
end
-
-
1
def options_card_name
-
options_rule_card&.name&.url_key || ":all"
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
def options_card_name
-
card.options_card_name
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def option_label option_name, id
-
42
%(<label for="#{id}">#{option_label_text option_name}</label>)
-
end
-
-
1
def option_view
-
42
@option_view ||= card.rule(:content_option_view) || :smart_label
-
end
-
-
1
def option_label_text option_name
-
42
return option_name unless (option_card = Card.fetch option_name)
-
-
42
nest option_card, view: option_view
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer/options_api.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Abstract; module Pointer;
-
# Set: Abstract (Pointer, OtherViews)
-
#
-
# BASE views
-
1
module OtherViews;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer/other_views.rb"; end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
def default_limit
-
16
20
-
end
-
-
1
def item_links args={}
-
card.item_cards(args).map do |item_card|
-
subformat(item_card).render_link
-
end
-
end
-
-
1
def nest_item_array
-
card.item_cards.map do |item|
-
nest_item item
-
end
-
end
-
-
1
view :core do
-
pointer_items.join ", "
-
end
-
-
1
def pointer_items args={}
-
16
page_args = args.extract! :limit, :offset
-
16
listing card.item_cards(page_args), args
-
end
-
end
-
-
# JavaScript views
-
-
2
module JsFormat; module_parent.send :register_set_format, Card::Format::JsFormat, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
nest_item_array.join "\n\n"
-
end
-
end
-
-
# Data views
-
-
2
module DataFormat; module_parent.send :register_set_format, Card::Format::DataFormat, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
nest_item_array
-
end
-
end
-
-
# JSON views
-
-
2
module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
-
1
view :content do
-
card.item_names
-
end
-
-
1
def item_cards
-
card.item_cards
-
end
-
-
1
def max_depth
-
params[:max_depth] || 1
-
end
-
-
1
def items_for_export
-
card.item_cards
-
end
-
-
1
def essentials
-
return {} if depth > max_depth
-
card.item_cards.map do |item|
-
nest item, view: :essentials
-
end
-
end
-
-
1
view :links do
-
[]
-
end
-
end
-
-
# CSS views
-
-
2
module CssFormat; module_parent.send :register_set_format, Card::Format::CssFormat, self; extend Card::Set::AbstractFormat
-
# generalize to all collections?
-
1
def default_item_view
-
:content
-
end
-
-
1
view :titled do
-
%(#{major_comment "STYLE GROUP: \"#{card.name}\"", '='}#{_render_core})
-
end
-
-
1
view :core do
-
nest_item_array.join "\n\n"
-
end
-
-
1
view :content, :core
-
end
-
-
# RSS views
-
-
2
module RssFormat; module_parent.send :register_set_format, Card::Format::RssFormat, self; extend Card::Set::AbstractFormat
-
1
def raw_feed_items
-
@raw_feed_items ||= card.item_cards(limit: limit, offset: offset)
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer/other_views.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (CodePointer)
-
#
-
1
module CodePointer;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/code_pointer.rb"; end
-
1
include_set Abstract::Pointer
-
-
1
abstract_basket :item_codenames
-
-
# simplify api
-
# Self::MyCodePointerSet.add_item :my_item_codename
-
# instead of
-
# Self::MyCodePointerSet.add_to_basket :item_codenames, :my_item_codename
-
1
module ClassMethods
-
1
def add_item codename
-
21
valid_codename codename do
-
21
add_to_basket :item_codenames, codename
-
end
-
end
-
-
1
def unshift_item codename
-
valid_codename codename do
-
unshift_basket :item_codenames, codename
-
end
-
end
-
-
1
def valid_codename codename
-
21
if Card::Codename.exist? codename
-
21
yield
-
else
-
Rails.logger.info "unknown codename '#{codename}' added to code pointer"
-
end
-
end
-
end
-
-
1
def content
-
item_codenames.map do |codename|
-
Card.fetch_name codename
-
end.compact.to_pointer_content
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/code_pointer.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (IdPointer)
-
#
-
# store items as ids, not names
-
1
module IdPointer;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/id_pointer.rb"; end
-
-
1
def standardize_item cardish
-
if (id = Card.fetch_id cardish)
-
"~#{id}"
-
else
-
Rails.logger.info "no id for '#{cardish}' added to id pointer"
-
nil
-
end
-
end
-
-
1
def item_ids args={}
-
item_strings(args).map do |item|
-
item = standardize_item item unless item.match?(/^~/)
-
item.to_s.tr("~", "").to_i
-
end.compact
-
end
-
-
1
def item_names args={}
-
item_ids(args).map(&:cardname).compact
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/id_pointer.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+ContentOptions" cards
-
#
-
1
module ContentOptions;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/right/content_options.rb"; end
-
1
def default_limit
-
4
cql_limit = fetch_query.limit if respond_to?(:fetch_query)
-
4
cql_limit || 50
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/right/content_options.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "InputOptions"
-
#
-
1
module InputOptions;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/self/input_options.rb"; end
-
1
include_set Abstract::Pointer
-
-
1
basket :options
-
1
add_to_basket :options, "radio"
-
1
add_to_basket :options, "checkbox"
-
1
add_to_basket :options, "select"
-
1
add_to_basket :options, "multiselect"
-
1
add_to_basket :options, "list"
-
1
add_to_basket :options, "filtered list"
-
-
1
def content
-
options.to_pointer_content
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/self/input_options.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "ScriptEditors"
-
#
-
1
module ScriptEditors;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/self/script_editors.rb"; end
-
1
include_set Abstract::CodePointer
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/self/script_editors.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "ScriptLibraries"
-
#
-
1
module ScriptLibraries;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/self/script_libraries.rb"; end
-
1
include_set Abstract::CodePointer
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/self/script_libraries.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "ScriptMods"
-
#
-
1
module ScriptMods;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/self/script_mods.rb"; end
-
1
include_set Abstract::CodePointer
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/self/script_mods.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "ScriptPointerConfig"
-
#
-
1
module ScriptPointerConfig;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/self/script_pointer_config.rb"; end
-
1
include_set Abstract::CodeFile
-
-
1
FILE_NAMES = %w[pointer_config pointer_list_editor]
-
-
1
def source_files
-
coffee_files FILE_NAMES
-
end
-
-
1
Self::ScriptEditors.add_item :script_pointer_config
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/self/script_pointer_config.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "StyleLibraries"
-
#
-
1
module StyleLibraries;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/self/style_libraries.rb"; end
-
1
include_set Abstract::CodePointer
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/self/style_libraries.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "StyleMods"
-
#
-
1
module StyleMods;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/self/style_mods.rb"; end
-
1
include_set Abstract::CodePointer
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/self/style_mods.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "LinkList" cards
-
#
-
1
module LinkList;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/type/link_list.rb"; end
-
1
include_set Abstract::Pointer
-
-
1
def raw_item_strings content
-
reference_chunks(content).map(&:referee_name)
-
end
-
-
1
def item_titles default_to_name=true
-
reference_chunks.map do |chunk|
-
chunk.options[:title] || (default_to_name ? chunk.referee_name : nil)
-
end
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
def chunk_list
-
:references
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def input_type
-
:link_list
-
end
-
-
1
view :link_list_input, cache: :never do
-
link_list_input
-
end
-
-
1
def items_for_input items=nil
-
items ||= card.item_names context: :raw
-
items.empty? ? [["", ""]] : items.zip(card.item_titles(false))
-
end
-
-
1
def link_list_input args={}
-
items = items_for_input args[:item_list]
-
extra_class = "pointer-link-list-ul"
-
ul_classes = classy "pointer-list-editor", extra_class
-
haml :link_list_input, items: items, ul_classes: ul_classes,
-
options_card: options_card_name
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/type/link_list.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "List" cards
-
#
-
1
module List;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/type/list.rb"; end
-
1
include_set Abstract::Pointer
-
-
1
def each_reference_out
-
53
item_names.each do |name|
-
475
yield(name, Card::Content::Chunk::Link::CODE)
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :view_list do
-
%i[info_bar bar box closed titled labeled].map do |view|
-
voo.items[:view] = view
-
wrap_with :p, [content_tag(:h3, "#{view} items"), render_content]
-
end
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/type/list.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "MirrorList" cards
-
#
-
1
module MirrorList;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/type/mirror_list.rb"; end
-
1
include_set Abstract::Pointer
-
-
1
event :validate_listed_by_name, :validate, on: :save, changing: :name do
-
if !junction? || !right || right.type_id != Card::CardtypeID
-
errors.add :name, tr(:cardtype_right)
-
end
-
end
-
-
1
event :validate_listed_by_content, :validate,
-
on: :save, changing: :content do
-
item_cards(content: content).each do |item_card|
-
next unless item_card.type_id != right.id
-
errors.add(
-
:content,
-
"#{item_card.name} has wrong cardtype; " \
-
"only cards of type #{name.right} are allowed"
-
)
-
end
-
end
-
-
1
event :update_content_in_list_cards, :prepare_to_validate,
-
on: :save, changing: :content do
-
return unless db_content.present?
-
new_items = item_keys(content: db_content)
-
old_items = item_keys(content: old_content)
-
remove_items(old_items - new_items)
-
add_items(new_items - old_items)
-
end
-
-
1
def old_content
-
db_content_before_act.present? ? db_content_before_act : content_cache.read(key)
-
end
-
-
1
def remove_items items
-
items.each do |item|
-
next unless (lc = list_card item)
-
lc.drop_item name.left
-
subcards.add lc
-
end
-
end
-
-
1
def add_items items
-
items.each do |item|
-
if (lc = list_card(item))
-
lc.add_item name.left
-
subcards.add lc
-
else
-
subcards.add(name: "#{Card[item].name}+#{left.type_name}",
-
type: "list",
-
content: "[[#{name.left}]]")
-
end
-
end
-
end
-
-
1
def content_cache
-
Card::Cache[Card::Set::Type::MirrorList]
-
end
-
-
1
def content
-
content_cache.fetch(key) do
-
generate_content
-
end
-
end
-
-
1
def generate_content
-
listed_by.map do |item|
-
"[[%s]]" % item.to_name.left
-
end.join "\n"
-
end
-
-
1
def listed_by
-
Card.search(
-
{ type_id: Card::MirroredListID, right: trunk.type_name,
-
left: { type: name.tag }, refer_to: name.trunk, return: :name },
-
"all cards listed by #{name}"
-
)
-
end
-
-
1
def update_cached_list
-
if trunk
-
Card::Cache[Card::Set::Type::MirrorList].write key, generate_content
-
else
-
Card::Cache[Card::Set::Type::MirrorList].delete key
-
end
-
end
-
-
1
def list_card item
-
Card.fetch item, left.type_name
-
end
-
-
1
def unfilled?
-
false
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/type/mirror_list.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "MirroredList" cards
-
#
-
1
module MirroredList;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/type/mirrored_list.rb"; end
-
1
include_set Abstract::Pointer
-
-
1
event :validate_list_name, :validate, on: :save, changed: :name do
-
errors.add :name, tr(:cardtype_right) unless right&.type_id == Card::CardtypeID
-
end
-
-
1
event :validate_list_item_type_change, :validate,
-
on: :save, changed: :name do
-
item_cards.each do |item_card|
-
next unless item_card.type_name.key != item_type_name.key
-
errors.add :name, tr(:conflict_item_type)
-
end
-
end
-
-
1
event :validate_list_content, :validate,
-
on: :save, changed: :content do
-
item_cards.each do |item_card|
-
next unless item_card.type_name.key != item_type_name.key
-
errors.add :content, tr(
-
:only_type_allowed,
-
cardname: item_card.name,
-
cardtype: name.right
-
)
-
end
-
end
-
-
1
event :create_listed_by_cards, :prepare_to_validate,
-
on: :save, changed: :content do
-
item_names.each do |item_name|
-
listed_by_name = "#{item_name}+#{left.type_name}"
-
next if director.main_director.card.key == listed_by_name.to_name.key
-
if !Card[listed_by_name]
-
add_subcard listed_by_name, type_id: Card::MirrorListID
-
else
-
Card[listed_by_name].update_references_out
-
end
-
end
-
end
-
-
1
event :update_related_listed_by_card_on_create, :finalize,
-
on: :create do
-
update_listed_by_cache_for item_keys
-
end
-
-
1
event :update_related_listed_by_card_on_content_update, :finalize,
-
on: :update, changed: :content do
-
new_items = item_keys
-
changed_items =
-
if db_content_before_act
-
old_items = item_keys(content: db_content_before_act)
-
old_items + new_items - (old_items & new_items)
-
else
-
new_items
-
end
-
update_listed_by_cache_for changed_items
-
end
-
-
1
event :update_related_listed_by_card_on_name_and_type_changes, :finalize,
-
on: :update, changed: %i[name type_id] do
-
update_all_items
-
end
-
-
1
event :update_related_listed_by_card_on_delete, :finalize,
-
on: :delete, when: proc { |c| c.junction? } do
-
update_listed_by_cache_for item_keys, type_key: @left_type_key
-
end
-
-
1
event :cache_type_key, :store,
-
on: :delete, when: proc { |c| c.junction? } do
-
@left_type_key = left.type_card.key
-
end
-
-
1
def update_all_items
-
current_items = item_keys
-
if db_content_before_act
-
old_items = item_keys(content: db_content_before_act)
-
update_listed_by_cache_for old_items
-
end
-
update_listed_by_cache_for current_items
-
end
-
-
1
def update_listed_by_cache_for item_keys, args={}
-
type_key = args[:type_key] || left&.type_card&.key
-
return unless type_key
-
-
item_keys.each do |item_key|
-
key = "#{item_key}+#{type_key}"
-
next unless Card::Cache[Card::Set::Type::MirrorList].exist? key
-
if (card = Card.fetch(key)) && card.left
-
card.update_cached_list
-
card.update_references_out
-
else
-
Card::Cache[Card::Set::Type::MirrorList].delete key
-
end
-
end
-
end
-
-
1
def item_type
-
name.right
-
end
-
-
1
def item_type_name
-
name.right_name
-
end
-
-
1
def item_type_card
-
name.right
-
end
-
-
1
def item_type_id
-
right.id
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/type/mirrored_list.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "NestList" cards
-
#
-
1
module NestList;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/type/nest_list.rb"; end
-
1
include_set Abstract::Items
-
-
1
def raw_item_strings content
-
reference_chunks(content).map(&:referee_name)
-
end
-
-
1
def item_options
-
nest_chunks.map(&:raw_options)
-
end
-
-
1
def items_to_content array
-
items = array.map { |i| standardize_item i }.reject(&:blank?)
-
self.content = items.join("\n")
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
def chunk_list
-
:references
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def input_type
-
:nest_list
-
end
-
-
1
view :nest_list_input, cache: :never do
-
nest_list_input
-
end
-
-
1
view :input do
-
_render_hidden_content_field + super()
-
end
-
-
1
def items_for_input items=nil
-
items ||= card.item_names context: :raw
-
items.empty? ? [["", ""]] : items.zip(card.item_options)
-
end
-
-
1
def nest_list_input args={}
-
items = items_for_input args[:item_list]
-
extra_class = "_nest-list-ul"
-
ul_classes = classy "pointer-list-editor", extra_class
-
haml :nest_list_input, items: items, ul_classes: ul_classes
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/type/nest_list.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Pointer" cards
-
#
-
1
module Pointer;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/type/pointer.rb"; end
-
1
include_set Abstract::Pointer
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :view_list do
-
%i[info_bar bar box closed titled labeled].map do |view|
-
voo.items[:view] = view
-
wrap_with :p, [content_tag(:h3, "#{view} items"), render_content]
-
end
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/type/pointer.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (VirtualCache)
-
#
-
# -*- encoding : utf-8 -*-
-
1
module VirtualCache;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-virtual/set/abstract/virtual_cache.rb"; end
-
-
1
def virtual?
-
new?
-
end
-
-
1
def history?
-
false
-
end
-
-
1
def followable?
-
false
-
end
-
-
1
def db_content
-
Card::Virtual.fetch_content(self)
-
end
-
-
# called to refresh the virtual content
-
# the default way is to use the card's template content
-
1
def generate_virtual_content
-
template&.db_content
-
end
-
-
1
event :save_virtual_content, :prepare_to_store, on: :save, changed: :content do
-
Card::Virtual.create_or_update(self, attributes["db_content"])
-
abort :success
-
end
-
-
1
event :delete_virtual_content, :prepare_to_store, on: :delete do
-
Card::Virtual.find_by_card(self)&.delete
-
abort :success
-
end
-
-
1
def delete
-
# delete although it's new
-
update trash: true
-
end
-
-
1
def delete!
-
# delete although it's new
-
update! trash: true
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-virtual/set/abstract/virtual_cache.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (Machine)
-
#
-
# ## What are Machines?
-
# {Machine} and {MachineInput} together implement a kind of observer pattern.
-
# {Machine} processes a collection of input cards to generate an output card
-
# (a {Set::Type::File} card by default). If one of the input cards is changed
-
# the output card will be updated.
-
#
-
# The classic example: A style card observes a collection of css and sccs card
-
# to generate a file card with a css file that contains the assembled
-
# compressed css.
-
#
-
# ## Using Machines
-
# Include the Machine module in the card set that is supposed to produce the
-
# output card. If the output card should be automatically updated when a input
-
# card is changed the input card has to be in a set that includes the
-
# MachineInput module.
-
#
-
# The default machine:
-
1
module Machine;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/machine.rb"; end
-
#
-
# - uses its item cards as input cards or the card itself if there are no
-
# item cards;
-
# - can be changed by passing a block to collect_input_cards
-
# - takes the raw view of the input cards to generate the output;
-
# - can be changed by passing a block to machine_input (in the input card
-
# set)
-
# - stores the output as a .txt file in the '+machine output' card;
-
# - can be changed by passing a filetype and/or a block to
-
# store_machine_output
-
#
-
#
-
# ## How does it work?
-
# Machine cards have a '+machine input' and a '+machine output' card. The
-
# '+machine input' card is a pointer to all input cards. Including the
-
# MachineInput module creates an 'on: save' event that runs the machines of
-
# all cards that are linked to that card via the +machine input pointer.
-
1
module MachineClassMethods
-
1
attr_accessor :output_config
-
-
1
def collect_input_cards &block
-
define_method :engine_input, &block
-
end
-
-
1
def prepare_machine_input &block
-
define_method :before_engine, &block
-
end
-
-
1
def machine_engine &block
-
define_method :engine, &block
-
end
-
-
1
def store_machine_output args={}, &block
-
5
output_config.merge!(args)
-
5
return unless block_given?
-
define_method :after_engine, &block
-
end
-
end
-
-
1
card_accessor :machine_output, type: FileID
-
1
card_accessor :machine_input, type: PointerID
-
-
1
def before_engine
-
end
-
-
1
def engine_input
-
ei = EngineInput.new self
-
ei.process
-
ei.new_input
-
end
-
-
# TODO: replace with call of extended_item_cards
-
# traverse through all levels of pointers and
-
# collect all item cards as input
-
1
class EngineInput
-
1
attr_accessor :new_input
-
-
1
def initialize machine_card
-
@machine_card = machine_card
-
@items = [machine_card]
-
@new_input = []
-
@extended = {}
-
@loop_limit = 5
-
end
-
-
1
def process
-
each_valid_item do
-
input_item = simple_item? ? @item : pointer_item
-
new_input << input_item if input_item
-
end
-
end
-
-
1
def simple_item?
-
@item.item_cards == [@item] # no pointer card
-
end
-
-
1
def pointer_item
-
@items.insert 0, @item.item_cards.reject(&:unknown?)
-
@items.flatten!
-
record_item
-
@item if @item != @machine_card && @item.known?
-
end
-
-
1
def record_item
-
@extended[@item] = @extended[@item].to_i + 1
-
end
-
-
1
def each_valid_item
-
until @items.empty?
-
@item = @items.shift
-
yield unless invalid_item?
-
end
-
end
-
-
1
def invalid_item?
-
@item.trash || @extended[@item.id].to_i > @loop_limit
-
end
-
end
-
-
1
def engine input
-
input
-
end
-
-
1
def after_engine output
-
filetype = output_config[:filetype]
-
file = Tempfile.new [id.to_s, ".#{filetype}"]
-
file.write output
-
file.rewind
-
Card::Auth.as_bot do
-
p = machine_output_card
-
p.file = file
-
p.save!
-
end
-
file.close
-
file.unlink
-
end
-
-
1
view :machine_output_url do
-
machine_output_url
-
end
-
-
1
class << self
-
1
def included host_class
-
6
host_class.extend(MachineClassMethods)
-
6
host_class.mattr_accessor :output_config
-
6
host_class.output_config = { filetype: "txt" }
-
-
6
define_machine_events host_class
-
end
-
-
1
def define_machine_events host_class
-
6
event_suffix = host_class.name.tr ":", "_"
-
6
event_name = "reset_machine_output_#{event_suffix}".to_sym
-
6
host_class.event event_name, after: :expire_related, changed: :content, on: :save do
-
reset_machine_output
-
end
-
end
-
end
-
-
1
include_set Abstract::Lock
-
-
1
def run_machine joint="\n"
-
before_engine
-
output =
-
input_item_cards.map do |input_card|
-
run_engine input_card
-
end.select(&:present?).join(joint)
-
after_engine output
-
end
-
-
1
def direct_machine_input? input_card
-
!input_card.collection? ||
-
input_card.respond_to?(:machine_input)
-
end
-
-
1
def run_engine input_card
-
return unless direct_machine_input? input_card
-
if (cached = fetch_cache_card(input_card)) && cached.content?
-
return cached.content
-
end
-
-
engine(input_from_card(input_card)).tap do |output|
-
cache_output_part input_card, output
-
end
-
end
-
-
1
def input_from_card input_card
-
if input_card.respond_to? :machine_input
-
input_card.machine_input
-
else
-
input_card.format._render_raw
-
end
-
end
-
-
1
def make_machine_output_coded mod=:machines
-
update_machine_output
-
Card::Auth.as_bot do
-
ENV["STORE_CODED_FILES"] = "true"
-
machine_output_card.update! storage_type: :coded, mod: mod,
-
codename: machine_output_codename
-
ENV["STORE_CODED_FILES"] = nil
-
end
-
end
-
-
1
def machine_output_codename
-
machine_output_card.name.parts.map do |part|
-
Card[part].codename&.to_s || Card[part].name.safe_key
-
end.join "_"
-
end
-
-
1
def input_item_cards
-
machine_input_card.item_cards
-
end
-
-
1
def machine_output_url
-
896
ensure_machine_output
-
896
machine_output_card.file.url # (:default, timestamp: false)
-
# to get rid of additional number in url
-
end
-
-
1
def machine_output_path
-
ensure_machine_output
-
machine_output_card.file.path
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/machine.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Abstract; module Machine;
-
# Set: Abstract (Machine, OutputCache)
-
#
-
1
module OutputCache;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/machine/output_cache.rb"; end
-
1
def fetch_cache_card input_card, new=nil
-
new &&= { type_id: PlainTextID }
-
Card.fetch input_card.name, name, :machine_cache, new: new
-
end
-
-
1
def cache_output_part input_card, output
-
Auth.as_bot do
-
# save virtual cards first
-
# otherwise the cache card will save it to get the left_id
-
# and trigger the cache update again
-
input_card.save! if input_card.new_card?
-
-
cache_card = fetch_cache_card(input_card, true)
-
cache_card.update! content: output
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/machine/output_cache.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Abstract; module Machine;
-
# Set: Abstract (Machine, OutputUpdate)
-
#
-
1
module OutputUpdate;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/machine/output_update.rb"; end
-
1
def reset_machine_output
-
Auth.as_bot do
-
moc = machine_output_card
-
@updated_at = output_updated_at
-
moc.delete! if moc.real?
-
update_input_card
-
expire_if_source_file_changed @updated_at
-
end
-
end
-
-
1
def regenerate_machine_output
-
return unless ok?(:read)
-
lock { run_machine }
-
end
-
-
1
def update_machine_output
-
return unless ok?(:read)
-
lock do
-
update_input_card
-
expire_if_source_file_changed output_updated_at
-
run_machine
-
end
-
end
-
-
1
def ensure_machine_output
-
896
output = fetch :machine_output
-
896
return if output&.selected_content_action_id
-
update_machine_output
-
end
-
-
1
def update_input_card
-
if Card::Director.running_act?
-
input_card = attach_subcard! machine_input_card
-
input_card.content = ""
-
engine_input.each { |input| input_card << input }
-
else
-
machine_input_card.items = engine_input
-
end
-
end
-
-
1
def input_cards_with_changed_source output_updated
-
machine_input_card.extended_item_cards.select do |i_card|
-
i_card.try(:source_changed?, since: output_updated)
-
end
-
end
-
-
1
def expire_if_source_file_changed output_updated_at
-
return unless output_updated_at
-
changed = input_cards_with_changed_source(output_updated_at)
-
return if changed.empty?
-
changed.each(&:expire_machine_cache)
-
true
-
end
-
-
# regenerates the machine output if a source file of a input card has been changed
-
1
def update_if_source_file_changed
-
return unless expire_if_source_file_changed output_updated_at
-
regenerate_machine_output
-
end
-
-
1
def output_updated_at
-
return unless (output_card = machine_output_card)
-
if output_card.coded?
-
File.mtime output_card.file.path
-
else
-
output_card.updated_at
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/machine/output_update.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (MachineInput)
-
#
-
1
module MachineInput;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/machine_input.rb"; end
-
1
module MachineInputClassMethods
-
1
attr_accessor :machines_cql
-
-
1
def machine_input_for args
-
@machines_cql = args
-
end
-
-
1
def machine_input &block
-
7
define_method :machine_input, block
-
end
-
end
-
-
1
def self.included host_class
-
4
host_class.extend(MachineInputClassMethods)
-
4
host_class.machines_cql = {}
-
4
host_class.machine_input do
-
format._render_raw
-
end
-
4
event_suffix = host_class.name.tr ":", "_"
-
4
define_update_event event_suffix, host_class
-
4
define_delete_events event_suffix, host_class
-
end
-
-
1
def self.define_delete_events event_suffix, host_class
-
4
event_name = "before_machine_input_deleted_#{event_suffix}".to_sym
-
4
host_class.event event_name, :store, on: :delete do
-
# exclude self because it's on the way to the trash
-
# otherwise it will be created again with the reset_machine_output
-
# call in the event below
-
@involved_machines =
-
MachineInput.search_involved_machines(name, host_class)
-
.reject { |card| card == self }
-
end
-
4
event_name = "after_machine_input_deleted_#{event_suffix}".to_sym
-
4
host_class.event event_name, :finalize, on: :delete do
-
expire_machine_cache
-
@involved_machines.each do |item|
-
item.reset_machine_output if item.is_a? Machine
-
end
-
end
-
end
-
-
1
def self.define_update_event event_suffix, host_class
-
4
host_class.event(
-
"after_machine_input_updated_#{event_suffix}".to_sym, :integrate,
-
on: :save
-
) do
-
expire_machine_cache
-
MachineInput.search_involved_machines(name, host_class)
-
.each do |item|
-
item.reset_machine_output if item.is_a? Machine
-
end
-
end
-
end
-
-
1
def self.search_involved_machines name, host_class
-
cql_statement =
-
{ right_plus: [
-
{ codename: "machine_input" },
-
{ link_to: name }
-
] }.merge(host_class.machines_cql)
-
Card.search(cql_statement)
-
end
-
-
1
def expire_machine_cache
-
Card.search(right_plus: [{ codename: "machine_input" }, { link_to: name }],
-
return: :name).each do |machine_name|
-
cache_card = Card.fetch(name, machine_name, :machine_cache)
-
next unless cache_card&.content?
-
Auth.as_bot { cache_card.update! trash: true }
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/machine_input.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (Script)
-
#
-
# -*- encoding : utf-8 -*-
-
1
module Script;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/script.rb"; end
-
-
1
require "uglifier"
-
-
1
def self.included host_class
-
2
host_class.include_set Abstract::Machine
-
2
host_class.include_set Abstract::MachineInput
-
-
2
host_class.machine_input { standard_machine_input }
-
2
host_class.store_machine_output filetype: "js"
-
end
-
-
1
def standard_machine_input
-
js = format(:js)._render_core
-
js = compress_js js if compress_js?
-
comment_with_source js
-
end
-
-
1
def comment_with_source js
-
"//#{name}\n#{js}"
-
end
-
-
1
def compress_js input
-
Uglifier.compile input
-
rescue => e
-
# CoffeeScript is compiled in a view
-
# If there is a CoffeeScript syntax error we get the rescued view here
-
# and the error that the rescued view is no valid Javascript
-
# To get the original error we have to refer to Card::Error.current
-
raise Card::Error, compression_error_message(e)
-
end
-
-
1
def compression_error_message e
-
if Card::Error.current
-
Card::Error.current.message
-
else
-
"JavaScript::SyntaxError (#{name}): #{e.message}"
-
end
-
end
-
-
1
def compress_js?
-
Cardio.config.compress_javascript
-
end
-
-
1
def clean_html?
-
false
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
def chunk_list # turn off autodetection of uri's
-
:nest_only
-
end
-
-
# def default_nest_view
-
# :raw
-
# end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def input_type
-
:ace_editor
-
end
-
-
1
def ace_mode
-
:javascript
-
end
-
-
1
def content_changes action, diff_type, hide_diff=false
-
wrap_with(:pre) { super }
-
end
-
-
1
view :core do
-
script = card.format(:js).render_core
-
process_content highlight(script)
-
end
-
-
1
def highlight script
-
::CodeRay.scan(script, :js).div
-
end
-
end
-
-
1
def diff_args
-
{ diff_format: :text }
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/script.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (SkinBox)
-
#
-
1
module SkinBox;
-
1
extend Card::Set
-
2
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/skin_box.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :box do
-
class_up "box-middle", "p-0"
-
voo.hide :customize_button, :box_middle
-
super()
-
end
-
-
1
view :box_bottom, template: :haml
-
-
1
view :customize_button, cache: :never do
-
customize_button
-
end
-
-
1
def customize_button target: parent&.card, text: "Apply and customize"
-
7
return "" unless card.codename.present?
-
7
theme = card.codename.match(/^(?<theme_name>.+)_skin$/).capture(:theme_name)
-
7
link_to_card target, text,
-
path: { action: :update, card: { content: "[[#{card.name}]]" },
-
customize: true, theme: theme },
-
class: "btn btn-sm btn-outline-primary mr-2"
-
end
-
-
1
view :box_middle do
-
return unless card.field(:image)
-
field_nest(:image, view: :full_width, size: :large)
-
end
-
-
1
def select_button target=parent.card
-
link_to_card target, "Apply",
-
path: { action: :update, card: { content: "[[#{card.name}]]" } },
-
class: "btn btn-sm btn-primary"
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/skin_box.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (ResetMachines)
-
#
-
1
module ResetMachines;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/all/reset_machines.rb"; end
-
1
module ClassMethods
-
1
def reset_script_machine
-
Auth.as_bot do
-
card = Card[:all, :script, :machine_output]
-
if card
-
card.update_columns trash: true
-
card.expire
-
Card::Virtual.where(right_id: MachineCacheID).delete_all
-
end
-
end
-
end
-
-
1
def reset_all_machines
-
Auth.as_bot do
-
Card.search(right: { codename: "machine_output" }).each do |card|
-
card.update_columns trash: true
-
card.expire
-
end
-
Card::Virtual.where(right_id: MachineCacheID).delete_all
-
end
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/all/reset_machines.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+MachineCache" cards
-
#
-
1
module MachineCache;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/right/machine_cache.rb"; end
-
1
include_set Abstract::VirtualCache
-
-
1
def clean_html?
-
false
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/right/machine_cache.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+MachineInput" cards
-
#
-
1
module MachineInput;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/right/machine_input.rb"; end
-
1
def followable?
-
false
-
end
-
-
1
def history?
-
false
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/right/machine_input.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+MachineOutput" cards
-
#
-
1
module MachineOutput;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/right/machine_output.rb"; end
-
1
def followable?
-
false
-
end
-
-
1
def ok_to_read
-
2
left.ok_to_read
-
end
-
-
1
def history?
-
false
-
end
-
-
1
event :remove_codename, :prepare_to_validate,
-
on: :delete,
-
when: proc { |c| c.codename.present? } do
-
# load file before deleting codename otherwise it will fail later
-
attachment
-
self.codename = nil
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
view :not_found do
-
if update_machine_output_live?
-
Card::Cache.reset_all # FIXME: wow, this is overkill, no?
-
root.error_status = 302
-
card.left.update_machine_output
-
card_path card.left.machine_output_url
-
else
-
super()
-
end
-
end
-
-
1
def update_machine_output_live?
-
case
-
when !card.left.is_a?(Abstract::Machine) then false # must be a machine
-
when card.left.locked? then false # machine must not be running
-
when card.new_card? then true # always update if new
-
else
-
# must want current output (won't re-output old stuff)
-
(selected_id = card.selected_action_id) &&
-
selected_id == card.last_action_id
-
end
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/right/machine_output.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "ScriptDecko"
-
#
-
1
module ScriptDecko;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/self/script_decko.rb"; end
-
1
include_set Abstract::CodeFile
-
-
1
def source_files
-
%w[mod editor name_editor autosave doubleclick layout navbox upload
-
slot modal overlay recaptcha slotter bridge
-
nest_editor nest_editor_rules nest_editor_options nest_editor_name
-
link_editor
-
components decko follow card_menu slot_ready
-
filter filter_links filter_items selectable_filtered_content].map do |n|
-
"decko/#{n}.js.coffee"
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/self/script_decko.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "ScriptHtml5shivPrintshiv"
-
#
-
1
module ScriptHtml5shivPrintshiv;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/self/script_html5shiv_printshiv.rb"; end
-
1
include_set Abstract::CodeFile
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :script_tag, perms: :none do
-
224
<<-HTML.strip_heredoc
-
<!--[if lt IE 9]>
-
#{javascript_include_tag card.machine_output_url}
-
<![endif]-->
-
HTML
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/self/script_html5shiv_printshiv.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "ScriptJquery"
-
#
-
1
module ScriptJquery;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/self/script_jquery.rb"; end
-
1
include_set Abstract::CodeFile
-
-
1
def source_files
-
["vendor/jquery_rails/vendor/assets/javascripts/jquery3.js",
-
"vendor/jquery_rails/vendor/assets/javascripts/jquery_ujs.js"]
-
end
-
-
1
def source_dir
-
""
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/self/script_jquery.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "ScriptJqueryHelper"
-
#
-
1
module ScriptJqueryHelper;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/self/script_jquery_helper.rb"; end
-
1
include_set Abstract::CodeFile
-
1
Self::ScriptLibraries.add_item :script_jquery_helper
-
-
1
def source_files
-
# jquery-ui includes all interaction components, the dialog and the autocomplete widget
-
# and all dependencies for those
-
# decko depends on autocomplete, sortable, jquery.autosize and jquery.fileupload
-
# the dialog widget is not used in decko but in wikirate
-
# don't know if iframe-transport is needed but it used to be there
-
%w[jquery-ui.js
-
jquery.autosize.js
-
../../vendor/jquery_file_upload/js/jquery.fileupload.js
-
../../vendor/jquery_file_upload/js/jquery.iframe-transport.js]
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/self/script_jquery_helper.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "StyleBootstrapCompatible"
-
#
-
1
module StyleBootstrapCompatible;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/self/style_bootstrap_compatible.rb"; end
-
1
include_set Abstract::CodeFile
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/self/style_bootstrap_compatible.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "StyleCards"
-
#
-
1
module StyleCards;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/self/style_cards.rb"; end
-
1
include_set Abstract::CodeFile
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/self/style_cards.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "StyleJqueryUiSmoothness"
-
#
-
1
module StyleJqueryUiSmoothness;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/self/style_jquery_ui_smoothness.rb"; end
-
1
include_set Abstract::CodeFile
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/self/style_jquery_ui_smoothness.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "CoffeeScript" cards
-
#
-
# -*- encoding : utf-8 -*-
-
1
module CoffeeScript;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/type/coffee_script.rb"; end
-
-
1
require "coffee-script"
-
-
1
include_set Abstract::Script
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def ace_mode
-
:coffee
-
end
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
compile_coffee _render_raw
-
end
-
-
1
def compile_coffee script
-
::CoffeeScript.compile script
-
rescue => e
-
line_nr = e.to_s.match(/\[stdin\]:(\d*)/)&.capture(0)&.to_i
-
line = script.lines[line_nr - 1] if line_nr
-
raise Card::Error, "CoffeeScript::Error (#{card.name}): #{e.message}: #{line}"
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/type/coffee_script.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Css" cards
-
#
-
# -*- encoding : utf-8 -*-
-
1
module Css;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/type/css.rb"; end
-
-
1
require "sassc"
-
1
require "benchmark"
-
-
1
include_set Abstract::Machine
-
1
include_set Abstract::MachineInput
-
-
1
store_machine_output filetype: "css"
-
-
1
machine_input do
-
compress_css format(format: :css)._render_core
-
end
-
-
1
def compress_css input
-
compress_css? ? SassC::Engine.new(input, style: :compressed).render : input
-
rescue => e
-
raise Card::Error, css_compression_error(e)
-
end
-
-
1
def css_compression_error error
-
# scss is compiled in a view
-
# If there is a scss syntax error we get the rescued view here
-
# and the error that the rescued view is no valid css
-
# To get the original error we have to refer to Card::Error.current
-
if Card::Error.current
-
Card::Error.current.message
-
else
-
"Sass::SyntaxError (#{name}): #{error.message}"
-
end
-
end
-
-
1
def clean_html?
-
false
-
end
-
-
1
def compress_css?
-
return true
-
!Rails.env.development?
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
# def default_nest_view
-
# :raw
-
# end
-
-
1
def chunk_list # turn off autodetection of uri's
-
:references
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def input_type
-
:ace_editor
-
end
-
-
1
def ace_mode
-
:css
-
end
-
-
1
def default_nest_view
-
:closed
-
end
-
-
1
view :core do
-
# FIXME: scan must happen before process for inclusion interactions to
-
# work, but this will likely cause
-
# problems with including other css?
-
process_content ::CodeRay.scan(_render_raw, :css).div, size: :icon
-
end
-
-
1
def content_changes action, diff_type, hide_diff=false
-
wrap_with(:pre) { super }
-
end
-
end
-
-
2
module CssFormat; module_parent.send :register_set_format, Card::Format::CssFormat, self; extend Card::Set::AbstractFormat
-
1
view :import do
-
%{\n@import url("#{_render_url}");\n}
-
end
-
end
-
-
1
def diff_args
-
{ diff_format: :text }
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/type/css.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "JavaScript" cards
-
#
-
# -*- encoding : utf-8 -*-
-
1
module JavaScript;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/type/java_script.rb"; end
-
-
1
include_set Abstract::Script
-
-
2
module JsFormat; module_parent.send :register_set_format, Card::Format::JsFormat, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
_render_raw
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/type/java_script.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Scss" cards
-
#
-
1
module Scss;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/type/scss.rb"; end
-
1
include_set Type::Css
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
view :core, cache: :never do
-
compile_scss(process_content(_render_raw))
-
end
-
-
1
def compile_scss scss, style=:expanded
-
SassC::Engine.new(scss, style: style).render
-
rescue SassC::SyntaxError => e
-
raise Card::Error,
-
"SassC::SyntaxError (#{card.name}:#{e.sass_backtrace}): #{e.message}"
-
# "#{#scss.lines[e.sass_line - 1]}\n" \
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def ace_mode
-
:scss
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/type/scss.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Skin" cards
-
#
-
1
module Skin;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/type/skin.rb"; end
-
1
include_set Abstract::MachineInput
-
1
include_set Abstract::SkinBox
-
1
include_set Pointer
-
-
1
def machine_input
-
# only the item of a skin card contribute input to the machine
-
# not the skin card itself
-
""
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/type/skin.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (Permission)
-
#
-
1
module Permission;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/abstract/permission.rb"; end
-
-
1
def standardize_items
-
1
super unless content == "_left"
-
end
-
-
1
def options_rule_card
-
303
Card[:cards_with_account]
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :pointer_core do
-
wrap_with :div, pointer_items, class: "pointer-list"
-
end
-
-
1
view :core, cache: :never do
-
if card.content == "_left"
-
core_inherit_content
-
else
-
render! :pointer_core
-
end
-
end
-
-
1
view :one_line_content, cache: :never do
-
render_core items: { view: :link }
-
end
-
-
1
view :input do
-
item_names = inheriting? ? [] : card.item_names
-
%(
-
#{_render_hidden_content_field}
-
<div class="perm-editor">
-
#{inheritance_checkbox}
-
<div class="perm-group perm-vals perm-section">
-
<h5 class="text-muted">Groups</h5>
-
#{groups item_names}
-
</div>
-
-
<div class="perm-indiv perm-vals perm-section">
-
<h5 class="text-muted">Individuals</h5>
-
#{list_input item_list: item_names, extra_css_class: 'perm-indiv-ul'}
-
</div>
-
</div>
-
)
-
end
-
-
1
private
-
-
1
def groups item_names
-
group_options.map do |option|
-
checked = !item_names.delete(option.name).nil?
-
icon = icon_tag "open_in_new", "link-muted"
-
option_link = link_to_card option.name, icon, target: "decko_role"
-
box = check_box_tag "#{option.key}-perm-checkbox",
-
option.name, checked, class: "perm-checkbox-button"
-
<<-HTML
-
<div class="form-check checkbox">
-
<label class="form-check-label">
-
#{box} #{option.name} #{option_link}
-
</label>
-
</div>
-
HTML
-
end * "\n"
-
end
-
-
1
def group_options
-
Auth.as_bot do
-
Card.search({ type_id: RoleID, sort: "name" }, "roles by name")
-
end
-
end
-
-
1
def inheritable?
-
@inheritable ||=
-
begin
-
set_name = card.name.trunk_name
-
set_card = Card.fetch(set_name)
-
not_set = set_card && set_card.type_id != SetID
-
not_set ? false : set_card.inheritable?
-
end
-
end
-
-
1
def inheriting?
-
@inheriting ||= inheritable? && card.content == "_left"
-
end
-
-
1
def inheritance_checkbox
-
return unless inheritable?
-
<<-HTML
-
<div class="perm-inheritance perm-section">
-
#{check_box_tag 'inherit', 'inherit', inheriting?}
-
<label>
-
#{core_inherit_content}
-
#{wrap_with(:a, title: "use left's #{card.name.tag} rule") { '?' }}
-
</label>
-
</div>
-
HTML
-
end
-
-
1
def core_inherit_content
-
text = if in_context_of_self_set?
-
core_inherit_for_content_for_self_set
-
else
-
"Inherit from left card"
-
end
-
%(<span class="inherit-perm">#{text}</span>)
-
end
-
-
1
def in_context_of_self_set?
-
return false unless @set_context
-
@set_context.to_name.tag_name.key == Card[:self].key
-
end
-
-
1
def core_inherit_for_content_for_self_set
-
task = card.tag.codename
-
ancestor = Card[@set_context.trunk_name.trunk_name]
-
links = ancestor.who_can(task).map do |card_id|
-
link_to_card card_id, nil, target: args[:target]
-
end * ", "
-
"Inherit ( #{links} )"
-
rescue
-
"Inherit"
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/abstract/permission.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (TemplatedNests)
-
#
-
1
module TemplatedNests;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/abstract/templated_nests.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
13
with_nest_mode :template do
-
13
super()
-
end
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/abstract/templated_nests.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (SupportsContentOptions)
-
#
-
1
module SupportsContentOptions;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/all/supports_content_options.rb"; end
-
1
def supports_content_options?
-
false
-
end
-
-
1
def supports_content_option_view?
-
false
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/all/supports_content_options.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Autoname" cards
-
#
-
1
module Autoname;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/autoname.rb"; end
-
1
def history?
-
false
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/autoname.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Comment" cards
-
#
-
1
module Comment;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/comment.rb"; end
-
1
include_set Abstract::Permission
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/comment.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+ContentOptionView" cards
-
#
-
1
module ContentOptionView;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/content_option_view.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def quick_edit
-
if card.left.prototype_default_card&.try(:show_content_options?) &&
-
card.left.prototype.rule_card(:input_type)&.supports_content_option_view?
-
super
-
else
-
""
-
end
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/content_option_view.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+ContentOptions" cards
-
#
-
1
module ContentOptions;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/content_options.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def quick_edit
-
card.left.prototype_default_card.try(:show_content_options?) ? super : ""
-
end
-
-
1
def quick_editor
-
wrap_type_formgroup do
-
type_field class: "type-field rule-type-field _submit-on-select"
-
end +
-
wrap_content_formgroup do
-
text_field :content, class: "d0-card-content _submit-after-typing"
-
end
-
end
-
-
1
def visible_cardtype_groups
-
{ "Organize" => %w[List Pointer] }
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/content_options.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Create" cards
-
#
-
1
module Create;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/create.rb"; end
-
1
include_set Abstract::Permission
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/create.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Default" cards
-
#
-
1
module Default;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/default.rb"; end
-
1
include_set Abstract::TemplatedNests
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :one_line_content do
-
raw = _render_raw
-
"#{card.type_name} : #{raw.present? ? raw : '<em>empty</em>'}"
-
end
-
-
1
def quick_form_opts
-
super.merge "data-update-foreign-slot":
-
".card-slot.quick_edit-view.RIGHT-Xinput_type,"\
-
".card-slot.quick_edit-view.RIGHT-Xcontent_option"\
-
".card-slot.quick_edit-view.RIGHT-Xcontent_option_view"
-
end
-
-
1
def quick_editor
-
wrap_type_formgroup do
-
type_field class: "type-field rule-type-field _submit-on-select"
-
end +
-
wrap_content_formgroup do
-
text_field :content, class: "d0-card-content _submit-after-typing"
-
end
-
end
-
-
1
def visible_cardtype_groups
-
hash = ::Card::Set::Self::Cardtype::GROUP.slice("Text", "Data", "Upload")
-
hash["Organize"] = ["List", "Pointer", "Link list", "Nest list"]
-
hash
-
end
-
end
-
-
1
def empty_ok?
-
true
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/default.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Delete" cards
-
#
-
1
module Delete;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/delete.rb"; end
-
1
include_set Abstract::Permission
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/delete.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Guide" cards
-
#
-
# include_set Abstract::TemplatedNests
-
1
module Guide;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/guide.rb"; end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def raw_help_text
-
# LOCALIZE
-
"Appears in the full editor view to guide users."
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/guide.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Help" cards
-
#
-
1
module Help;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/help.rb"; end
-
1
include_set Abstract::TemplatedNests
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :popover do
-
popover_link _render_core
-
end
-
-
1
def quick_editor
-
# TODO: refactor when voo.input_type is ready. (and use class_up)
-
formgroup "Content", input: :content, help: false do
-
text_field :content, value: card.content,
-
class: "d0-card-content _submit-after-typing"
-
end
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/help.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+InputType" cards
-
#
-
1
module InputType;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/input_type.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def quick_editor
-
@submit_on_change = true
-
super
-
end
-
-
1
def quick_form_opts
-
super.merge "data-update-foreign-slot":
-
".card-slot.quick_edit-view.RIGHT-Xcontent_option_view"
-
end
-
-
1
def default_input_type
-
:radio
-
end
-
-
1
def raw_help_text
-
6
"edit interface for list cards"
-
end
-
-
# def option_label_text option_name
-
# super.downcase
-
# end
-
-
1
def quick_edit
-
card.left.prototype_default_card.try(:show_input_type?) ? super : ""
-
end
-
end
-
-
1
def option_names
-
left.prototype_default_card&.try(:input_type_content_options) || super
-
end
-
-
1
def supports_content_option_view?
-
item_name.in? ["checkbox", "radio", "filtered list"]
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/input_type.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Read" cards
-
#
-
1
module Read;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/read.rb"; end
-
1
include Abstract::Permission
-
-
2
format :html do include Abstract::Permission::HtmlFormat end
-
-
1
event :cascade_read_rule, :finalize, after: :update_rule_cache, when: :is_rule? do
-
return unless name_is_changing? || trash_is_changing?
-
-
update_read_ruled_cards
-
end
-
-
1
def update_read_ruled_cards
-
Card::Rule.clear_read_rule_cache
-
Card.cache.reset # maybe be more surgical, just Auth.user related
-
expire # probably shouldn't be necessary,
-
# but was sometimes getting cached version when card should be in the
-
# trash. could be related to other bugs?
-
-
processed = update_read_rules_of_set_members
-
update_cards_with_read_rule_id processed unless new?
-
end
-
-
1
def update_read_rules_of_set_members
-
return unless rule_pattern_index
-
-
each_member do |member, processed|
-
processed << member.key
-
member.update_read_rule unless member_has_overriding_rule?(member)
-
end
-
end
-
-
1
def member_has_overriding_rule? member
-
pattern_index(Card.fetch_id(member.read_rule_class)) < rule_pattern_index
-
end
-
-
# cards with this card as a read_rule_id
-
# These may include cards that are no longer set members if the card was renamed
-
# (edge case)
-
1
def update_cards_with_read_rule_id processed
-
processed ||= ::Set.new
-
Card::Auth.as_bot do
-
Card.search(read_rule_id: id) do |card|
-
card.update_read_rule unless processed.include?(card.key)
-
end
-
end
-
end
-
-
1
def each_member
-
Auth.as_bot do
-
all_members.each_with_object(::Set.new) do |member, processed|
-
yield member, processed
-
end
-
end
-
end
-
-
1
def all_members
-
rule_set.item_cards limit: 0
-
end
-
-
1
def rule_pattern_index
-
return if trash
-
-
@rule_pattern_index ||= pattern_index rule_set&.tag&.id
-
end
-
-
1
def pattern_index pattern_id
-
pattern_ids.index(pattern_id) || invalid_pattern_id(pattern_id)
-
end
-
-
1
def pattern_ids
-
@pattern_ids ||= set_patterns.map(&:pattern_id)
-
end
-
-
1
def invalid_pattern_id pattern_id
-
Rails.logger.info "invalid pattern id for read rule: #{pattern_id}"
-
end
-
-
1
event :process_read_rule_update_queue, :finalize do
-
left&.update_read_rule
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/read.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Script" cards
-
#
-
1
module Script;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/script.rb"; end
-
1
include_set Abstract::Machine
-
-
1
store_machine_output filetype: "js"
-
-
1
def ok_to_read
-
1
true
-
end
-
-
1
view :javascript_include_tag do
-
%(
-
<script src="#{card.machine_output_url}" type="text/javascript"></script>
-
)
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def raw_help_text
-
"JavaScript (or CoffeeScript) for card's page."
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/script.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Structure" cards
-
#
-
1
module Structure;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/structure.rb"; end
-
1
include_set Abstract::TemplatedNests
-
-
2
module RssFormat; module_parent.send :register_set_format, Card::Format::RssFormat, self; extend Card::Set::AbstractFormat
-
1
def raw_feed_items
-
[card]
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :one_line_content do
-
"#{_render_type} : #{_render_raw}"
-
end
-
-
1
def visible_cardtype_groups
-
hash = ::Card::Set::Self::Cardtype::GROUP.slice("Text")
-
hash["Organize"] = ["Search", "Nest list"]
-
hash
-
end
-
end
-
-
1
event :update_structurees_references, :integrate,
-
when: :update_structurees_references? do
-
8
return unless (query = structuree_query)
-
-
8
Auth.as_bot do
-
8
query.run.each(&:update_references_out)
-
end
-
end
-
-
1
def update_structurees_references?
-
8
db_content_changed? || action == :delete
-
end
-
-
1
event :reset_cache_to_use_new_structure,
-
before: :update_structurees_references do
-
8
Card::Cache.reset_hard
-
8
Card::Cache.reset_soft
-
end
-
-
1
event :update_structurees_type, :finalize,
-
8
changed: :type_id, when: proc { |c| c.assigns_type? } do
-
update_structurees type_id: type_id
-
end
-
-
1
def structuree_names
-
8
return [] unless (query = structuree_query(return: :name))
-
-
8
Auth.as_bot do
-
8
query.run
-
end
-
end
-
-
1
def update_structurees args
-
# note that this is not smart about overriding templating rules
-
# for example, if someone were to change the type of a
-
# +*right+*structure rule that was overridden
-
# by a +*type plus right+*structure rule, the override would not be respected.
-
return unless (query = structuree_query(return: :id))
-
-
Auth.as_bot do
-
query.run.each_slice(100) do |id_batch|
-
Card.where(id: id_batch).update_all args
-
end
-
end
-
end
-
-
1
def structuree_query args={}
-
16
set_card = trunk
-
16
return unless set_card.type_id == SetID
-
-
16
set_card.fetch_query args
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/structure.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Style" cards
-
#
-
1
module Style;
-
1
extend Card::Set
-
2
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/style.rb"; end
-
1
require "sassc"
-
1
include_set Abstract::Machine
-
-
1
store_machine_output filetype: "css"
-
-
1
def ok_to_read
-
225
true
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
# turn off autodetection of uri's
-
1
def chunk_list
-
:nest_only
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
HIDDEN_SKINS = %w[bootstrap_default_skin themeless_bootstrap_skin bootstrap_default_skin
-
classic_bootstrap_skin].freeze
-
-
1
def default_item_view
-
:bar
-
end
-
-
1
view :input, template: :haml
-
-
1
def themes
-
card.rule_card(:content_options).item_cards
-
end
-
-
1
def selectable_themes
-
themes.reject do |theme_card|
-
theme_card.right&.codename == :stylesheets ||
-
theme_card.key.in?(HIDDEN_SKINS)
-
end
-
end
-
end
-
-
1
event :customize_theme, :prepare_to_validate, on: :update, when: :customize_theme? do
-
skin_name = free_skin_name
-
add_subcard skin_name, type_id: CustomizedBootswatchSkinID
-
self.content = "[[#{skin_name}]]"
-
end
-
-
1
def free_skin_name
-
name = "#{@theme} skin customized"
-
if Card.exist?(name)
-
name = "#{name} 1"
-
name.next! while Card.exist?(name)
-
end
-
name
-
end
-
-
1
def customize_theme?
-
Env.params[:customize].present? && (@theme = Env.params[:theme]).present?
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/style.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Update" cards
-
#
-
1
module Update;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/update.rb"; end
-
1
include_set Abstract::Permission
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/update.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Autoname"
-
#
-
1
module Autoname;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/autoname.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :templating, position: 4,
-
help_text: "Autogenerate name for new cards by incrementing this value. "\
-
"[[http://decko.org/autonaming|more]]"
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/autoname.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Captcha"
-
#
-
1
module Captcha;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/captcha.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :permission, position: 5,
-
help_text: "Anti-spam setting. Requires non-signed-in users to complete a "\
-
"[[http://decko.org/captcha|captcha]] before adding or editing "\
-
"cards (where permitted)."
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/captcha.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "ContentOptionView"
-
#
-
1
module ContentOptionView;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/content_option_view.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :editing, position: 6,
-
restricted_to_type: %i[list pointer session],
-
rule_type_editable: false,
-
help_text: "Label view for radio button and checkbox items. "\
-
"[[http://decko.org/Pointer|more]]",
-
applies: lambda { |prototype|
-
prototype.supports_content_options? &&
-
prototype.rule_card(:input_type)&.supports_content_option_view?
-
}
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/content_option_view.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "ContentOptions"
-
#
-
1
module ContentOptions;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/content_options.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :editing, position: 5,
-
restricted_to_type: %i[list pointer session],
-
rule_type_editable: true,
-
help_text: "Value options for [[List]] and [[Pointer]] and cards. "\
-
"Can itself be a List or a [[Search]]. "\
-
"[[http://decko.org/Pointer|more]]",
-
applies: lambda { |prototype|
-
prototype.rule_card(:input_type).supports_content_options?
-
}
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/content_options.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Create"
-
#
-
1
module Create;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/create.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :permission, position: 1, rule_type_editable: false,
-
short_help_text: "who can create cards",
-
help_text: "Who can create new cards"
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/create.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "CsvStructure"
-
#
-
1
module CsvStructure;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/csv_structure.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :templating, position: 3, rule_type_editable: true
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/csv_structure.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Default"
-
#
-
1
module Default;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/default.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :templating, position: 2, rule_type_editable: true,
-
short_help_text: "type/content template for new cards"
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/default.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "DefaultHtmlView"
-
#
-
1
module DefaultHtmlView;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/default_html_view.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :other, position: 4, rule_type_editable: false
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/default_html_view.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Delete"
-
#
-
1
module Delete;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/delete.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :permission, position: 4, rule_type_editable: false,
-
short_help_text: "who can delete cards",
-
help_text: "Who can delete cards"
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/delete.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "FollowFields"
-
#
-
1
module FollowFields;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/follow_fields.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :other, position: 5, rule_type_editable: false
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/follow_fields.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Guide"
-
#
-
1
module Guide;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/guide.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :editing, position: 3, rule_type_editable: true,
-
short_help_text: "appears in the full editor view to guide users"
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/guide.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Head"
-
#
-
1
module Head;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/head.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :webpage, position: 1, rule_type_editable: false,
-
short_help_text: "head tag content",
-
help_text: "head tag content"
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/head.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Help"
-
#
-
1
module Help;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/help.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :editing, position: 1, rule_type_editable: true,
-
short_help_text: "help text people will see when editing",
-
help_text: "[[http://decko.org/custom_help_text|Help text]] "\
-
"people will see when editing."
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/help.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "InputType"
-
#
-
1
module InputType;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/input_type.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :editing,
-
position: 3,
-
rule_type_editable: false,
-
short_help_text: "edit interface"
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def raw_help_text
-
"Configure [[https://ace.c9.io/|ace]], "\
-
"Decko's default code editor, using these available "\
-
"[[https://github.com/ajaxorg/ace/wiki/Configuring-Ace|options]]."
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/input_type.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Layout"
-
#
-
1
module Layout;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/layout.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :webpage, position: 3, rule_type_editable: false,
-
help_text: "HTML structure of card's page "\
-
"[[http://decko.org/layouts | more]]"
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/layout.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "OnCreate"
-
#
-
1
module OnCreate;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/on_create.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :event, position: 1, rule_type_editable: false,
-
help_text: "Configure events to be executed when card is created"
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/on_create.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "OnDelete"
-
#
-
1
module OnDelete;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/on_delete.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :event, position: 3, rule_type_editable: false,
-
help_text: "Configure events to be executed when card is deleted"
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/on_delete.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "OnUpdate"
-
#
-
1
module OnUpdate;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/on_update.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :event, position: 2, rule_type_editable: false,
-
help_text: "Configure events to be executed when card is updated"
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/on_update.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Read"
-
#
-
1
module Read;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/read.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :permission, position: 2, rule_type_editable: false,
-
short_help_text: "who can view cards",
-
help_text: "Who can view cards in the [[set]]."
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/read.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "RecentSettings"
-
#
-
1
module RecentSettings;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/recent_settings.rb"; end
-
1
def history?
-
111
false
-
end
-
-
1
def followable?
-
false
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/recent_settings.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Script"
-
#
-
1
module Script;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/script.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :webpage, position: 5
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/script.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Structure"
-
#
-
1
module Structure;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/structure.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :templating, position: 2, rule_type_editable: true,
-
short_help_text: "control card's content / structure",
-
help_text: "Controls cards' content / structure. "\
-
"[[http://decko.org/formatting|more]]"
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/structure.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Style"
-
#
-
1
module Style;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/style.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :webpage, position: 4,
-
help_text: "Skin (collection of stylesheets) for card's page. "\
-
"[[http://decko.org/skins|more]]"
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/style.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "TableOfContents"
-
#
-
1
module TableOfContents;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/table_of_contents.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :other, position: 1, rule_type_editable: false,
-
help_text: "Autogenerate [[http://decko.org/table_of_contents|"\
-
"table of contents]] on cards with at least this many headers "\
-
'("0" means never).'
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/table_of_contents.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Thanks"
-
#
-
1
module Thanks;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/thanks.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :other, position: 3, rule_type_editable: false,
-
short_help_text: "destination after card is created",
-
help_text: "Destination after card is created. "\
-
"[[http://decko.org/Custom_thank_you_messages_for_forms|more]]"
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/thanks.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Update"
-
#
-
1
module Update;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/update.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :permission, position: 3, rule_type_editable: false,
-
short_help_text: "who can update cards",
-
help_text: "Who can update cards"
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/update.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Setting" cards
-
#
-
# require "json"
-
1
module Setting;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/type/setting.rb"; end
-
-
1
def self.member_names
-
@@member_names ||= begin
-
Card.search(
-
{ type_id: SettingID, return: "key" },
-
"all setting cards"
-
).each_with_object({}) do |card_key, hash|
-
hash[card_key] = true
-
end
-
end
-
end
-
-
2
module DataFormat; module_parent.send :register_set_format, Card::Format::DataFormat, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
cql = { left: { type: SetID },
-
right: card.id,
-
limit: 0 }
-
Card.search(cql).compact.map { |c| nest c }
-
end
-
end
-
-
1
def count
-
19
Card.search left: { type: SetID }, right: id, limit: 0, return: :count
-
end
-
-
1
def set_classes_with_rules
-
Card.set_patterns.reverse.map do |set_class|
-
cql = { left: { type: SetID },
-
right: id,
-
sort: %w[content name],
-
limit: 0 }
-
cql[:left][(set_class.anchorless? ? :id : :right_id)] = set_class.pattern_id
-
-
rules = Card.search cql
-
[set_class, rules] unless rules.empty?
-
end.compact
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def rule_link rule, text
-
link_to_card rule, text, path: { view: :modal_rule },
-
slotter: true, "data-modal-class": "modal-lg"
-
end
-
-
1
view :core do
-
haml do
-
<<-'HAML'.strip_heredoc
-
= _render_rule_help
-
%h3 All #{card.name.tr "*", ""} rules that apply to
-
- card.set_classes_with_rules.each do |klass, rules|
-
%p
-
%h5
-
= klass.generic_label.downcase
-
- if klass.anchorless?
-
= nest rules.first, view: :bar, show: :full_name
-
- else
-
- rules.each do |rule|
-
= nest rule, view: :bar
-
HAML
-
end
-
end
-
-
# Because +*help content renders in "template" mode when you render its content
-
# directly, we render the help text in the context of the *all+<setting> card
-
1
view :rule_help do
-
nest [:all, card.name], view: :rule_help
-
end
-
-
1
view :one_line_content do
-
render_rule_help
-
end
-
end
-
-
2
module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
-
1
def items_for_export
-
Card.search left: { type: SetID }, right: card.id, limit: 0
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/type/setting.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (FilterHelper)
-
#
-
# TODO: move sort/filter handling out of card and into base format
-
1
module FilterHelper;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/abstract/00_filter_helper.rb"; end
-
# sorting and filtering is about viewing the data, not altering the data itself.
-
-
1
def sort_hash
-
266
sort_param.present? ? { sort: sort_param } : {}
-
end
-
-
1
def filter_param field
-
filter_hash[field.to_sym]
-
end
-
-
# FIXME: it is inconsistent that #sort_hash has :sort as the key, but
-
# #filter_hash is the _value_ of the hash with :filter as the key.
-
1
def filter_hash
-
266
@filter_hash ||=
-
247
Env.params[:filter].present? ? Env.hash(Env.params[:filter]) : default_filter_hash
-
end
-
-
1
def sort_param
-
266
@sort_param ||= safe_sql_param :sort
-
end
-
-
1
def safe_sql_param key
-
266
param = Env.params[key]
-
266
param.blank? ? nil : Card::Query.safe_sql(param)
-
end
-
-
1
def filter_keys_with_values
-
filter_keys.map do |key|
-
values = filter_param(key)
-
values.present? ? [key, values] : next
-
end.compact
-
end
-
-
# initial values for filtered search
-
1
def default_filter_hash
-
247
{}
-
end
-
-
1
def offset
-
param_to_i :offset, 0
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
delegate :filter_hash, :sort_hash, :filter_param, :sort_param,
-
:all_filter_keys, to: :card
-
-
1
def extra_paging_path_args
-
19
super.merge filter_and_sort_hash
-
end
-
-
1
def filter_and_sort_hash
-
19
sort_hash.merge filter: filter_hash
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/abstract/00_filter_helper.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (SearchParams)
-
#
-
1
module SearchParams;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/abstract/02_search_params.rb"; end
-
1
include_set Abstract::PagingParams
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
def offset
-
29
search_params[:offset] || 0
-
end
-
-
1
def search_params
-
72
@search_params ||= default_search_params
-
end
-
-
# used for override
-
1
def default_search_params
-
11
if (qparams = query_params)
-
4
paging_params.merge vars: qparams
-
else
-
7
paging_params
-
end
-
end
-
-
1
def paging_params
-
11
{ limit: limit_param, offset: offset_param }
-
end
-
-
1
def query_params
-
36
return nil unless (vars = params[:query])
-
-
29
Env.hash vars
-
end
-
-
1
def default_limit
-
100
-
end
-
-
1
def extra_paging_path_args
-
19
return {} unless (vars = query_params)
-
-
19
{ query: vars }
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def default_limit
-
10
Cardio.config.paging_limit || 20
-
end
-
end
-
-
2
module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
-
1
def default_limit
-
20
-
end
-
end
-
-
2
module RssFormat; module_parent.send :register_set_format, Card::Format::RssFormat, self; extend Card::Set::AbstractFormat
-
1
def default_limit
-
25
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/abstract/02_search_params.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (Filter)
-
#
-
1
module Filter;
-
1
extend Card::Set
-
3
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/abstract/03_filter.rb"; end
-
1
include_set Abstract::Utility
-
-
1
def filter_class
-
Card::FilterQuery
-
end
-
-
1
def filter_keys
-
[:name]
-
end
-
-
1
def filter_keys_from_params
-
filter_hash.keys.map(&:to_sym) - [:not_ids]
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def sort_options
-
{ "Alphabetical": :name, "Recently Added": :create }
-
end
-
-
1
view :filtered_content, template: :haml, wrap: :slot
-
-
1
view :selectable_filtered_content, template: :haml, cache: :never
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/abstract/03_filter.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Abstract; module Filter;
-
# Set: Abstract (Filter, FilterForm)
-
#
-
1
module FilterForm;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/abstract/03_filter/filter_form.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
# sort and filter ui
-
1
view :filter_form, cache: :never do
-
filter_fields slot_selector: "._filter-result-slot",
-
sort_field: _render(:sort_formgroup)
-
end
-
-
1
view :quick_filters, cache: :never do
-
return "" unless quick_filter_list.present?
-
-
haml :quick_filters, filter_list: normalized_quick_filter_list
-
end
-
-
1
def normalized_quick_filter_list
-
quick_filter_list.map do |hash|
-
hash = hash.clone
-
filter_key = hash.keys.first
-
{
-
text: (hash.delete(:text) || hash[filter_key]),
-
icon: (hash.delete(:icon) || mapped_icon_tag(filter_key)),
-
# FIXME: mapped_icon_tag is a wikirate concept
-
class: css_classes(hash.delete(:class),
-
"_filter-link quick-filter-by-#{filter_key}"),
-
filter: JSON(hash[:filter] || hash)
-
}
-
end
-
end
-
-
# for override
-
1
def quick_filter_list
-
[]
-
end
-
-
# for override
-
1
def custom_quick_filters
-
""
-
end
-
-
# @param data [Hash] the filter categories. The hash needs for every category
-
# a hash with a label and a input_field entry.
-
1
def filter_form data={}, sort_input_field=nil, form_args={}
-
haml :filter_form, categories: data,
-
sort_input_field: sort_input_field,
-
form_args: form_args
-
end
-
-
1
def filter_fields slot_selector: nil, sort_field: nil
-
form_args = { action: filter_action_path, class: "slotter" }
-
form_args["data-slot-selector"] = slot_selector if slot_selector
-
filter_form filter_form_data, sort_field, form_args
-
end
-
-
1
def filter_form_data
-
all_filter_keys.each_with_object({}) do |cat, h|
-
h[cat] = { label: filter_label(cat),
-
input_field: _render("filter_#{cat}_formgroup"),
-
active: active_filter?(cat),
-
default: default_filter?(cat) }
-
end
-
end
-
-
1
def active_filter? field
-
if card.filter_keys_from_params.present?
-
filter_hash.key? field
-
else
-
default_filter? field
-
end
-
end
-
-
1
def default_filter? field
-
card.default_filter_hash.key? field
-
end
-
-
1
def filter_label field
-
# return "Keyword" if field.to_sym == :name
-
#
-
filter_label_from_method(field) || filter_label_from_name(field)
-
end
-
-
1
def filter_label_from_method field
-
try "#{field}_filter_label"
-
end
-
-
1
def filter_label_from_name field
-
Card.fetch_name(field) { field.to_s.titleize }
-
end
-
-
1
def filter_action_path
-
path
-
end
-
-
1
view :sort_formgroup, cache: :never do
-
select_tag "sort",
-
options_for_select(sort_options, card.current_sort),
-
class: "pointer-select _filter-sort form-control",
-
"data-minimum-results-for-search": "Infinity"
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/abstract/03_filter/filter_form.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Abstract; module Filter;
-
# Set: Abstract (Filter, FormHelper)
-
#
-
1
module FormHelper;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/abstract/03_filter/form_helper.rb"; end
-
1
include_set Abstract::FilterHelper
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :filter_name_formgroup, cache: :never do
-
text_filter :name
-
end
-
-
1
def select_filter field, default=nil, options=nil
-
options ||= filter_options field
-
options.unshift(["--", ""]) unless default
-
select_filter_tag field, default, options
-
end
-
-
1
def multiselect_filter field, default=nil, options=nil
-
options ||= filter_options field
-
multiselect_filter_tag field, default, options
-
end
-
-
1
def text_filter field, default=nil, opts={}
-
value = filter_param(field) || default
-
text_filter_with_name_and_value filter_name(field), value, opts
-
end
-
-
1
def text_filter_with_name_and_value name, value, opts
-
opts[:class] ||= "simple-text"
-
add_class opts, "form-control"
-
text_field_tag name, value, opts
-
end
-
-
1
def range_filter field, default={}, opts={}
-
add_class opts, "simple-text range-filter-subfield"
-
default ||= {}
-
output [range_sign(:from),
-
sub_text_filter(field, :from, default, opts),
-
range_sign(:to),
-
sub_text_filter(field, :to, default, opts)]
-
end
-
-
1
def range_sign side
-
dir = side == :from ? "right" : "left"
-
wrap_with :span, class: "input-group-prepend" do
-
fa_icon("chevron-circle-#{dir}", class: "input-group-text")
-
end
-
end
-
-
1
def sub_text_filter field, subfield, default={}, opts={}
-
name = "filter[#{field}][#{subfield}]"
-
value = filter_hash.dig(field, subfield) || default[subfield]
-
text_filter_with_name_and_value name, value, opts
-
end
-
-
1
def autocomplete_filter type_code, options_card=nil
-
options_card ||= Card::Name[type_code, :type, :by_name]
-
text_filter type_code, "", class: "#{type_code}_autocomplete",
-
"data-options-card": options_card
-
end
-
-
1
def multiselect_filter_tag field, default, options, html_options={}
-
html_options[:multiple] = true
-
select_filter_tag field, default, options, html_options
-
end
-
-
1
def select_filter_tag field, default, options, html_options={}
-
name = filter_name field, html_options[:multiple]
-
options = options_for_select options, (filter_param(field) || default)
-
normalize_select_filter_tag_html_options field, html_options
-
select_tag name, options, html_options
-
end
-
-
# alters html_options hash
-
1
def normalize_select_filter_tag_html_options field, html_options
-
pointer_suffix = html_options[:multiple] ? "multiselect" : "select"
-
add_class html_options, "pointer-#{pointer_suffix} filter-input #{field} " \
-
"_filter_input_field _no-select2 form-control"
-
# _no-select2 because select is initiated after filter is opened.
-
html_options[:id] = "filter-input-#{unique_id}"
-
end
-
-
1
def filter_name field, multi=false
-
"filter[#{field}]#{'[]' if multi}"
-
end
-
-
1
def filter_options field
-
raw = send("#{field}_options")
-
raw.is_a?(Array) ? raw : option_hash_to_array(raw)
-
end
-
-
1
def option_hash_to_array hash
-
hash.each_with_object([]) do |(key, value), array|
-
array << [key, value.to_s]
-
array
-
end
-
end
-
-
1
def type_options type_codename, order="asc", max_length=nil
-
Card.cache.fetch "#{type_codename}-TYPE-OPTIONS" do
-
res = Card.search type: type_codename, return: :name, sort: "name", dir: order
-
max_length ? (res.map { |i| [trim_option(i, max_length), i] }) : res
-
end
-
end
-
-
1
def trim_option option, max_length
-
option.size > max_length ? "#{option[0..max_length]}..." : option
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/abstract/03_filter/form_helper.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Abstract; module Filter;
-
# Set: Abstract (Filter, QueryConstruction)
-
#
-
# all filter keys in the order they were selected
-
1
module QueryConstruction;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/abstract/03_filter/query_construction.rb"; end
-
1
def all_filter_keys
-
@all_filter_keys ||= filter_keys_from_params | filter_keys
-
end
-
-
1
def filter_and_sort_cql
-
247
filter_cql.merge(sort_cql)
-
end
-
-
1
def filter_cql
-
247
return {} if filter_hash.empty?
-
-
filter_cql_from_params
-
end
-
-
# separate method is needed for tests
-
1
def filter_cql_from_params
-
filter_class.new(filter_keys_with_values, blocked_id_cql).to_cql
-
end
-
-
1
def sort_cql
-
247
sort_hash
-
end
-
-
1
def blocked_id_cql
-
not_ids = filter_param :not_ids
-
not_ids.present? ? { id: ["not in", not_ids.split(",")] } : {}
-
end
-
-
1
def current_sort
-
sort_param || default_sort_option
-
end
-
-
1
def default_sort_option
-
cql_content[:sort]
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/abstract/03_filter/query_construction.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (RightFilterForm)
-
#
-
# To be included in a field card to get a filter for the parent.
-
1
module RightFilterForm;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/abstract/04_right_filter_form.rb"; end
-
# The core view renders a filter for the left card.
-
-
1
include_set Set::Abstract::Filter
-
-
1
def virtual?
-
new?
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def filter_action_path
-
path mark: card.name.left, view: filter_view
-
end
-
-
1
view :core, cache: :never do
-
filter_fields slot_selector: filter_selector
-
end
-
-
1
def filter_view
-
:filter_result
-
end
-
-
1
def filter_selector
-
".#{filter_view}-view"
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/abstract/04_right_filter_form.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (Search)
-
#
-
1
module Search;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/abstract/05_search.rb"; end
-
1
include_set Abstract::Paging
-
1
include_set Abstract::SearchParams
-
1
include_set Abstract::Filter
-
-
1
def search _args={}
-
raise Error, "search not overridden"
-
end
-
-
1
def cached_search args={}
-
35
@search_results ||= {}
-
35
@search_results[args.to_s] ||= search args
-
end
-
-
1
def returning item, args
-
6
args[:return] = item
-
6
yield
-
end
-
-
1
def item_cards args={}
-
args[:limit] ||= 0
-
returning(:card, args) { search args }
-
end
-
-
1
def item_names args={}
-
4
args[:limit] ||= 0
-
8
returning(:name, args) { search args }
-
end
-
-
1
def item_ids args={}
-
args[:limit] ||= 0
-
returning(:id, args) { search args }
-
end
-
-
1
def count args={}
-
2
args[:offset] = 0
-
2
args[:limit] = 0
-
4
returning(:count, args) { search args }
-
end
-
-
# for override
-
1
def item_type
-
nil
-
end
-
-
1
def each_item_name_with_options _content=nil
-
options = {}
-
item = fetch_query.statement[:view]
-
options[:view] = item if item
-
item_names.each do |name|
-
yield name, options
-
end
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
def search_with_params
-
29
search_with_rescue search_params
-
end
-
-
1
def count_with_params
-
6
search_with_rescue search_params.merge(return: :count)
-
end
-
-
1
def search_with_rescue query_args
-
35
card.cached_search query_args
-
rescue Error::BadQuery => e
-
Rails.logger.info "BadQuery: #{query_args}"
-
e
-
end
-
-
1
def implicit_item_view
-
37
view = voo_items_view || item_view_from_query || default_item_view
-
37
Card::View.normalize view
-
end
-
-
# override if query can specify item view
-
1
def item_view_from_query
-
nil
-
end
-
-
1
def with_results
-
11
return render_no_search_results if search_with_params.empty?
-
4
yield
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/abstract/05_search.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Abstract; module Search;
-
# Set: Abstract (Search, Views)
-
#
-
1
module Views;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/abstract/05_search/views.rb"; end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
view :search_count, cache: :never do
-
search_with_params.to_s
-
end
-
-
1
view :search_error, cache: :never do
-
sr_class = search_with_params.class.to_s
-
%(#{sr_class} :: #{search_with_params.message} :: #{card.content})
-
end
-
-
1
view :card_list, cache: :never do
-
if search_with_params.empty?
-
"no results"
-
else
-
search_with_params.map do |item_card|
-
nest_item item_card
-
end.join "\n"
-
end
-
end
-
end
-
-
2
module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
-
1
AUTOCOMPLETE_LIMIT = 8 # number of name suggestions for autocomplete text fields
-
-
1
def item_cards
-
search_with_params
-
end
-
-
# NOCACHE because paging_urls is uncacheable hash and thus not safe to merge
-
1
view :molecule, cache: :never do
-
molecule.merge render_paging_urls
-
end
-
-
# TODO: design better autocomplete API
-
1
view :name_complete, cache: :never do
-
complete_search limit: AUTOCOMPLETE_LIMIT
-
end
-
-
1
view :name_match, cache: :never do
-
complete_or_match_search limit: AUTOCOMPLETE_LIMIT
-
end
-
-
1
def complete_or_match_search limit: AUTOCOMPLETE_LIMIT, start_only: false
-
3
starts_with = complete_search limit: limit
-
3
return starts_with if start_only
-
-
remaining_slots = limit - starts_with.size
-
return starts_with if remaining_slots.zero?
-
starts_with + match_search(not_names: starts_with, limit: remaining_slots)
-
end
-
-
1
def complete_search limit: AUTOCOMPLETE_LIMIT
-
3
card.search name_cql(limit).merge(complete_cql)
-
end
-
-
1
def match_search limit: AUTOCOMPLETE_LIMIT, not_names: []
-
card.search name_cql(limit).merge(match_cql(not_names))
-
end
-
-
1
def name_cql limit
-
3
{ limit: limit, sort: "name", return: "name" }
-
end
-
-
1
def complete_cql
-
3
{ complete: term_param }
-
end
-
-
1
def match_cql not_names
-
cql = { name_match: term_param }
-
cql[:name] = ["not in"] + not_names if not_names.any?
-
cql
-
end
-
-
1
def term_param
-
params[:term]
-
end
-
end
-
-
2
module DataFormat; module_parent.send :register_set_format, Card::Format::DataFormat, self; extend Card::Set::AbstractFormat
-
1
view :card_list do
-
search_with_params.map do |item_card|
-
nest_item item_card
-
end
-
end
-
end
-
-
2
module CsvFormat; module_parent.send :register_set_format, Card::Format::CsvFormat, self; extend Card::Set::AbstractFormat
-
1
view :core, :core, mod: All::AllCsv::CsvFormat
-
-
1
view :card_list do
-
items = super()
-
if depth.zero?
-
title_row + items
-
else
-
items
-
end
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :card_list, cache: :never do
-
11
with_results do
-
4
search_result_list "search-result-list" do |item_card|
-
37
card_list_item item_card
-
end
-
end
-
end
-
-
1
view :select_item, cache: :never do
-
wrap do
-
haml :select_item
-
end
-
end
-
-
1
before :select_item do
-
class_up "card-slot", "_filter-result-slot"
-
end
-
-
1
view :checkbox_list, cache: :never do
-
with_results do
-
search_result_list "_search-checkbox-list pr-2" do |item_card|
-
checkbox_item item_card
-
end
-
end
-
end
-
-
1
view :no_search_results do
-
7
wrap_with :div, "", class: "search-no-results"
-
end
-
-
1
private
-
-
1
def card_list_item item_card
-
37
nest_item item_card, size: voo.size do |rendered, item_view|
-
37
%(<div class="search-result-item item-#{item_view}">#{rendered}</div>)
-
end
-
end
-
-
1
def search_result_list klass
-
4
with_paging do
-
4
wrap_with :div, class: klass do
-
4
search_with_params.map do |item_card|
-
37
yield item_card
-
end
-
end
-
end
-
end
-
-
1
def checkbox_item item_card
-
subformat(item_card).wrap do
-
haml :checkbox_item, unique_id: unique_id, item_card: item_card
-
end
-
end
-
-
1
def closed_limit
-
[search_params[:limit].to_i, Card.config.closed_search_limit].min
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/abstract/05_search/views.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (CqlSearch)
-
#
-
1
module CqlSearch;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/abstract/06_cql_search.rb"; end
-
1
include_set Abstract::Search
-
-
1
def search args={}
-
23
with_skipping args do
-
23
query = fetch_query(args)
-
# forces explicit limiting
-
# can be 0 or less to force no limit
-
23
raise "OH NO.. no limit" unless query.mods[:limit]
-
23
query.run
-
end
-
end
-
-
# for override, eg when required subqueries are known to be missing
-
1
def skip_search?
-
23
false
-
end
-
-
1
def with_skipping args
-
23
skip_search? ? skipped_search_result(args) : yield
-
end
-
-
1
def skipped_search_result args={}
-
args[:return] == :count ? 0 : []
-
end
-
-
1
def cache_query?
-
715
true
-
end
-
-
1
def fetch_query args={}
-
47
@query = nil unless cache_query?
-
47
@query ||= {}
-
47
@query[args.to_s] ||= query(args.clone) # cache query
-
end
-
-
1
def query args={}
-
43
Query.new standardized_query_args(args), name
-
end
-
-
1
def standardized_query_args args
-
43
args = query_args(args).symbolize_keys
-
43
args[:context] ||= name
-
43
args
-
end
-
-
1
def cql_hash
-
423
@cql_hash = nil unless cache_query?
-
423
@cql_hash ||= cql_content.merge filter_and_sort_cql
-
end
-
-
# override this to define search
-
1
def cql_content
-
245
@cql_content = nil unless cache_query?
-
245
@cql_content ||= begin
-
245
query = content
-
245
query = query.is_a?(Hash) ? query : parse_json_query(query)
-
245
query.symbolize_keys
-
end
-
end
-
-
1
def query_args args={}
-
41
cql_hash.merge args
-
end
-
-
1
def parse_json_query query
-
247
empty_query_error! if query.empty?
-
247
JSON.parse query
-
rescue JSON::ParserError
-
raise Error::BadQuery, "Invalid JSON search query: #{query}"
-
end
-
-
1
def empty_query_error!
-
raise Error::BadQuery, "can't run search with empty content"
-
end
-
-
1
def item_type
-
365
type = cql_hash[:type]
-
365
return if type.is_a?(Array) || type.is_a?(Hash)
-
365
type
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
def default_limit
-
card_content_limit || super
-
end
-
-
1
def card_content_limit
-
10
card.cql_hash&.dig :limit
-
end
-
-
1
def item_view_from_query
-
37
query_with_params.statement[:item]
-
end
-
-
1
def query_with_params
-
90
@query_with_params ||= card.fetch_query search_params
-
end
-
-
1
def limit
-
53
query_with_params.limit
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def default_limit
-
10
card_content_limit || super
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/abstract/06_cql_search.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Children" cards
-
#
-
1
module Children;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/children.rb"; end
-
1
def raw_help_text
-
"Cards formed by \"mating\" {{_left|name}} with another card. "\
-
"eg: \"{{_left|name}}+mate\"."
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/children.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Created" cards
-
#
-
1
module Created;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/created.rb"; end
-
1
def raw_help_text
-
"Cards created by {{_left|name}}."
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/created.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Edited" cards
-
#
-
1
module Edited;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/edited.rb"; end
-
1
def raw_help_text
-
"Cards edited by {{_left|name}}."
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/edited.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Editors" cards
-
#
-
1
module Editors;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/editors.rb"; end
-
1
def raw_help_text
-
"Users who have edited {{_left|name}}."
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/editors.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Follow" cards
-
#
-
1
module Follow;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/follow.rb"; end
-
1
def raw_help_text
-
"Get notified about changes"
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/follow.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+LinkedToBy" cards
-
#
-
1
module LinkedToBy;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/linked_to_by.rb"; end
-
1
def raw_help_text
-
"Cards that link to {{_left|name}}."
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/linked_to_by.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+LinksTo" cards
-
#
-
# -*- encoding : utf-8 -*-
-
1
module LinksTo;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/links_to.rb"; end
-
-
1
def raw_help_text
-
"Cards that <em>{{_left|name}}</em> links to."
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/links_to.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Mates" cards
-
#
-
1
module Mates;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/mates.rb"; end
-
1
def raw_helo_text
-
"If there is a card named \"X+{{_left|name}}\", then X is a mate of {{_left|name}}."
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/mates.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+NestedBy" cards
-
#
-
1
module NestedBy;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/nested_by.rb"; end
-
1
def raw_help_text
-
"Cards that include {{_left|name}}."
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/nested_by.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Nests" cards
-
#
-
1
module Nests;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/nests.rb"; end
-
1
def raw_help_text
-
"Cards that {{_left|name}} includes."
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/nests.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+ReferredToBy" cards
-
#
-
1
module ReferredToBy;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/referred_to_by.rb"; end
-
1
def raw_help_text
-
"Cards that refer to {{_left|name}}."
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/referred_to_by.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+RefersTo" cards
-
#
-
1
module RefersTo;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/refers_to.rb"; end
-
1
def raw_help_text
-
"Cards that {{_left|name}} refers to."
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/refers_to.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Recent"
-
#
-
1
module Recent;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/self/recent.rb"; end
-
1
ACTS_PER_PAGE = 25
-
-
1
view :title do
-
224
voo.title ||= "Recent Changes"
-
224
super()
-
end
-
-
1
def recent_acts
-
action_relation = qualifying_actions.where "card_acts.id = card_act_id"
-
Act.where("EXISTS (#{action_relation.to_sql})").order id: :desc
-
end
-
-
1
def qualifying_actions
-
Action.all_viewable.where "draft is not true"
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
voo.hide :history_legend unless voo.main
-
@acts_per_page = ACTS_PER_PAGE
-
acts_layout card.recent_acts, :absolute
-
end
-
end
-
-
2
module RssFormat; module_parent.send :register_set_format, Card::Format::RssFormat, self; extend Card::Set::AbstractFormat
-
1
view :feed_item_description do
-
render_blank
-
end
-
end
-
-
2
module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
-
1
def items_for_export
-
card.item_cards limit: 20
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/self/recent.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Search"
-
#
-
1
module Search;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/self/search.rb"; end
-
1
def query_args args={}
-
10
return super unless keyword_contains_cql? args
-
2
args.merge parse_keyword_cql(args)
-
end
-
-
1
def parse_keyword_cql args
-
2
parse_json_query(args[:vars][:keyword])
-
end
-
-
1
def keyword_contains_cql? hash
-
10
hash[:vars] && (keyword = hash[:vars][:keyword]) && keyword =~ /^\{.+\}$/
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
view :search_error, cache: :never do
-
sr_class = search_with_params.class.to_s
-
-
# don't show card content; not very helpful in this case
-
%(#{sr_class} :: #{search_with_params.message})
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :title, cache: :never do
-
4
return super() unless (keyword = search_keyword) &&
-
4
(title = keyword_search_title(keyword))
-
4
voo.title = title
-
end
-
-
1
def keyword_search_title keyword
-
4
%(Search results for: <span class="search-keyword">#{h keyword}</span>)
-
end
-
-
1
def search_keyword
-
4
(vars = search_vars) && vars[:keyword]
-
rescue Card::Error::PermissionDenied
-
nil
-
end
-
-
1
def search_vars
-
4
root.respond_to?(:search_params) ? root.search_params[:vars] : search_params[:vars]
-
end
-
-
1
def cql_search?
-
card.keyword_contains_cql? vars: search_vars
-
end
-
end
-
-
2
module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
-
1
view :complete, cache: :never do
-
3
term = term_param
-
3
exact = Card.fetch term, new: {}
-
-
{
-
3
search: true,
-
term: term,
-
add: add_item(exact),
-
new: new_item_of_type(exact),
-
goto: goto_items(term, exact)
-
}
-
end
-
-
1
def add_item exact
-
3
return unless exact.new_card? &&
-
exact.name.valid? &&
-
!exact.virtual? &&
-
exact.ok?(:create)
-
[h(exact.name), exact.name.url_key]
-
end
-
-
1
def new_item_of_type exact
-
3
return unless (exact.type_id == CardtypeID) &&
-
Card.new(type_id: exact.id).ok?(:create)
-
[exact.name, "new/#{exact.name.url_key}"]
-
end
-
-
1
def goto_items term, exact
-
3
goto_names = complete_or_match_search start_only: Card.config.navbox_match_start_only
-
3
goto_names.unshift exact.name if add_exact_to_goto_names? exact, goto_names
-
3
goto_names.map do |name|
-
6
[name, name.to_name.url_key, h(highlight(name, term, sanitize: false))]
-
end
-
end
-
-
1
def add_exact_to_goto_names? exact, goto_names
-
4
exact.known? && !goto_names.find { |n| n.to_name.key == exact.key }
-
end
-
-
1
def term_param
-
6
term = query_params[:keyword]
-
6
if (term =~ /^\+/) && (main = params["main"])
-
term = main + term
-
end
-
6
term
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/self/search.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "SearchType" cards
-
#
-
1
module SearchType;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/type/search_type.rb"; end
-
1
include_set Type::Json
-
1
include_set Abstract::CqlSearch
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
view :core, cache: :never do
-
_render search_result_view
-
end
-
-
1
def chunk_list
-
64
:query
-
end
-
-
1
def search_result_view
-
11
case search_with_params
-
when Exception then :search_error
-
when Integer then :search_count
-
when nest_mode == :template then :raw
-
11
else :card_list
-
end
-
end
-
end
-
-
2
module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
-
1
def items_for_export
-
return [] if card.content.empty? || unexportable_tag?(card.name.tag_name.key)
-
card.item_cards
-
end
-
-
# avoid running the search from +:content_options (huge results)
-
# and +:structure (errors)
-
# TODO: make this configurable in set mods
-
1
def unexportable_tag? tag_key
-
%i[content_options structure].map { |code| code.cardname.key }.include? tag_key
-
end
-
end
-
-
2
module RssFormat; module_parent.send :register_set_format, Card::Format::RssFormat, self; extend Card::Set::AbstractFormat
-
1
view :feed_body do
-
case raw_feed_items
-
when Exception then @xml.item(render!(:search_error))
-
when Integer then @xml.item(render!(:search_count))
-
else super()
-
end
-
end
-
-
1
def raw_feed_items
-
@raw_feed_items ||= search_with_params
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
11
_render search_result_view
-
end
-
-
1
view :bar do
-
voo.hide :one_line_content
-
super()
-
end
-
-
1
view :one_line_content, cache: :never do
-
if depth > max_depth
-
"..."
-
else
-
search_params[:limit] = closed_limit
-
_render_core hide: "paging", items: { view: :link }
-
# TODO: if item is queryified to be "name", then that should work.
-
# otherwise use link
-
end
-
end
-
-
1
def rss_link_tag
-
path_opts = { format: :rss }
-
Array(search_params[:vars]).compact.each { |k, v| opts["_#{k}"] = v }
-
tag "link", rel: "alternate",
-
type: "application/rss+xml",
-
title: "RSS",
-
href: path(path_opts)
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/type/search_type.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Comment)
-
#
-
1
module Comment;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/comment.rb"; end
-
1
def commenting?
-
297
comment && action != :delete
-
end
-
-
1
event :add_comment, :prepare_to_store, on: :save, when: :comment do
-
Env.session[:comment_author] = comment_author if Env.session
-
return unless comment.present?
-
self.content =
-
[content, format.comment_with_signature].compact.join "\n<hr\>\n"
-
self.comment = nil
-
end
-
-
1
attr_writer :comment_author
-
-
1
def comment_author
-
@comment_author ||=
-
Env.session[:comment_author] || Env.params[:comment_author] || "Anonymous"
-
end
-
-
1
def clean_comment
-
comment.split(/\n/).map do |line|
-
"<p>#{line.strip.empty? ? ' ' : line}</p>"
-
end * "\n"
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
def comment_with_signature
-
card.clean_comment + "\n" + comment_signature
-
end
-
-
1
def comment_signature
-
wrap_with :div, class: "w-comment-author" do
-
"#{comment_author}.....#{Time.zone.now}"
-
end
-
end
-
-
1
def comment_author
-
if Auth.signed_in?
-
"[[#{Auth.current.name}]]"
-
else
-
"#{card.comment_author} (Not signed in)"
-
end
-
end
-
-
1
view :comment_box, denial: :blank, unknown: true, perms: :update do
-
wrap_with :div, class: "comment-box nodblclick" do
-
action = card.new_card? ? :create : :update
-
card_form action do
-
[hidden_comment_fields, comment_box, comment_buttons]
-
end
-
end
-
end
-
-
1
def hidden_comment_fields
-
return unless card.new_card?
-
hidden_field_tag "card[name]", card.name
-
# FIXME: wish we had more generalized solution for names.
-
# without this, nonexistent cards will often take left's linkname.
-
# (needs test)
-
end
-
-
1
def comment_box
-
text_area :comment, rows: 3
-
end
-
-
1
def comment_buttons
-
wrap_with :div, class: "comment-buttons" do
-
[comment_author_label, comment_submit_button]
-
end
-
end
-
-
1
def comment_author_label
-
return if Auth.signed_in?
-
%(<label>My Name is:</label> #{text_field :comment_author})
-
end
-
-
1
def comment_submit_button
-
submit_button text: "Comment", type: :submit, disable_with: "Commenting"
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/comment.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Error)
-
#
-
1
module Error;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/error.rb"; end
-
1
def copy_errors card
-
card.errors.each do |att, msg|
-
errors.add att, msg
-
end
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
view :compact_missing, perms: :none, compact: true do
-
""
-
end
-
-
1
view :unknown, perms: :none, cache: :never do
-
""
-
end
-
-
1
view :server_error, perms: :none do
-
tr(:server_error)
-
end
-
-
1
view :denial, perms: :none do
-
focal? ? tr(:denial) : ""
-
end
-
-
1
view :not_found, perms: :none do
-
error_name = card.name.present? ? safe_name : tr(:not_found_no_name)
-
tr(:not_found_named, cardname: error_name)
-
end
-
-
1
view :bad_address, perms: :none do
-
root.error_status = 404
-
tr(:bad_address)
-
end
-
-
1
view :errors do
-
["Problem:", "", standard_errors].flatten.join "\n"
-
end
-
-
1
def standard_errors
-
card.errors.map do |attrib, msg|
-
attrib == :abort ? msg : standard_error_message(attrib, msg)
-
end
-
end
-
-
# for override
-
1
def standard_error_message attribute, message
-
"#{attribute.to_s.upcase}: #{message}"
-
end
-
-
1
def unsupported_view_error_message view
-
tr(:unsupported_view, view: view, cardname: card.name)
-
end
-
end
-
-
2
module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
-
1
view :errors do
-
format_error error_list
-
end
-
-
1
view :server_error, :errors
-
1
view :denial, :errors
-
1
view :not_found, :errors
-
-
1
view :bad_address do
-
format_error super()
-
end
-
-
1
def format_error error
-
{ error_status: error_status, errors: error }
-
end
-
-
1
def error_list
-
card.errors.each_with_object([]) do |(field, message), list|
-
list << { field: field, message: message }
-
end
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/error.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Links)
-
#
-
1
module Links;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/links.rb"; end
-
1
RESOURCE_TYPE_REGEXP = /^([a-zA-Z][\-+\.a-zA-Z\d]*):/
-
-
# The #link_to methods support smart formatting of links in multiple formats.
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
# Creates a "link", the meaning of which depends upon the format. In this base
-
# format, the link looks like [text][absolute path]
-
#
-
# @param text [String] optional string associated with link
-
# @param opts [Hash] optional Hash. In simple formats, :path is usually the only key
-
1
def link_to text=nil, opts={}
-
path = path((opts.delete(:path) || {}))
-
if text && path != text
-
"#{text}[#{path}]"
-
else
-
path
-
end
-
end
-
-
# link to a different view of the current card
-
# @param view [Symbol,String]
-
# @param text [String]
-
# @param opts [Hash]
-
1
def link_to_view view, text=nil, opts={}
-
791
add_to_path opts, view: view unless view == :home
-
791
link_to text, opts
-
end
-
-
# link to a card other than the current card.
-
# @param cardish [Integer, Symbol, String, Card] a card identifier
-
# @param text [String]
-
# @param opts [Hash]
-
1
def link_to_card cardish, text=nil, opts={}
-
2923
add_to_path opts, mark: Card::Name[cardish]
-
2923
link_to text, opts
-
end
-
-
# a "resource" is essentially a reference to something that
-
# decko doesn't recognize to be a card. Can be a remote url,
-
# a local url (that decko hasn't parsed) or a local path.
-
# @param resource [String]
-
# @param text [String]
-
# @param opts [Hash]
-
1
def link_to_resource resource, text=nil, opts={}
-
715
resource = clean_resource resource, resource_type(resource)
-
715
link_to text, opts.merge(path: resource)
-
end
-
-
# smart_link_to is wrapper method for #link_to, #link_to_card, #link_to_view, and
-
# #link_to_resource. If the opts argument contains :view, :related, :card, or
-
# :resource, it will use the respective method to render a link.
-
#
-
# This is usually most useful when writing views that generate many different
-
# kinds of links.
-
1
def smart_link_to text, opts={}
-
if (linktype = %i[view card resource].find { |key| opts[key] })
-
send "link_to_#{linktype}", opts.delete(linktype), text, opts
-
else
-
send :link_to, text, opts
-
end
-
end
-
-
1
private
-
-
1
def resource_type resource
-
1430
case resource
-
508
when /^https?\:/ then "external-link"
-
910
when %r{^/} then "internal-link"
-
12
when /^mailto\:/ then "email-link"
-
when RESOURCE_TYPE_REGEXP then Regexp.last_match(1) + "-link"
-
end
-
end
-
-
1
def clean_resource resource, resource_type
-
715
if resource_type == "internal-link"
-
455
contextualize_path resource[1..-1]
-
else
-
260
resource
-
end
-
end
-
-
1
def add_to_path opts, new_hash
-
3714
opts[:path] = (opts[:path] || {}).merge new_hash
-
end
-
end
-
-
1
public
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
# in HTML, #link_to renders an anchor tag <a>
-
# it treats opts other than "path" as html opts for that tag,
-
# and it adds special handling of "remote" and "method" opts
-
# (changes them into data attributes)
-
1
def link_to text=nil, opts={}
-
5028
opts[:href] ||= path opts.delete(:path)
-
5028
text = raw(text || opts[:href])
-
5028
interpret_data_opts_to_link_to opts
-
5028
wrap_with :a, text, opts
-
end
-
-
# in HTML, #link_to_card adds special css classes indicated whether a
-
# card is "known" (real or virtual) or "wanted" (unknown)
-
# TODO: upgrade from (known/wanted)-card to (real/virtual/unknown)-card
-
1
def link_to_card cardish, text=nil, opts={}
-
2923
name = Card::Name[cardish]
-
2923
slotterify opts if opts[:slotter]
-
2923
add_known_or_wanted_class opts, name
-
2923
super name, (text || name), opts
-
end
-
-
# in HTML, #link_to_view defaults to a remote link with rel="nofollow".
-
1
def link_to_view view, text=nil, opts={}
-
791
slotterify opts
-
791
super view, (text || view), opts
-
end
-
-
# in HTML, #link_to_resource automatically adds a target to external resources
-
# so they will open in another tab. It also adds css classes indicating whether
-
# the resource is internal or external
-
1
def link_to_resource resource, text=nil, opts={}
-
715
add_resource_opts opts, resource_type(resource)
-
715
super
-
end
-
-
1
private
-
-
1
def slotterify opts
-
791
opts.delete(:slotter)
-
791
opts.reverse_merge! remote: true, rel: "nofollow"
-
791
add_class opts, "slotter"
-
end
-
-
1
def add_known_or_wanted_class opts, name
-
2923
known = opts.delete :known
-
2923
known = Card.known?(name) if known.nil?
-
2923
add_class opts, (known ? "known-card" : "wanted-card")
-
end
-
-
1
def interpret_data_opts_to_link_to opts
-
5028
%i[remote method].each do |key|
-
10056
next unless (val = opts.delete key)
-
838
opts["data-#{key}"] = val
-
end
-
end
-
-
1
def add_resource_opts opts, resource_type
-
715
opts[:target] ||= "_blank" if resource_type == "external-link"
-
715
add_class opts, resource_type
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/links.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (ListChanges)
-
#
-
# -*- encoding : utf-8 -*-
-
1
module ListChanges;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/list_changes.rb"; end
-
-
1
def list_fields
-
1
Card.search({ left: name, type_id: Card::MirroredListID }, "list fields")
-
end
-
-
1
def listed_by_fields
-
1
Card.search({ left: name, type_id: Card::MirrorListID }, "listed by fields")
-
end
-
-
1
def linker_lists
-
136
Card.search({ type_id: Card::MirroredListID, link_to: name },
-
"lists that link to #{name}")
-
end
-
-
1
def codename_list_exist?
-
137
Card::Codename.exists?(:mirrored_list) && Card::Codename.exists?(:mirror_list)
-
end
-
-
1
event :trunk_cardtype_of_a_list_relation_changed, :finalize,
-
changed: :type, on: :update, when: :codename_list_exist? do
-
1
type_key_was = Card.quick_fetch(type_id_before_act)&.key
-
-
1
list_fields.each do |card|
-
card.update_listed_by_cache_for card.item_keys, type_key: type_key_was
-
card.update_listed_by_cache_for card.item_keys
-
end
-
1
listed_by_fields.each &:update_cached_list
-
end
-
-
1
event :trunk_name_of_a_list_relation_changed, :finalize,
-
changed: :name, on: :update,
-
when: :codename_list_exist? do
-
list_fields.each do |card|
-
card.update_listed_by_cache_for card.item_keys
-
end
-
listed_by_fields.each &:update_cached_list
-
end
-
-
1
event :cardtype_of_list_item_changed, :validate,
-
changed: :type, on: :save,
-
when: :codename_list_exist? do
-
136
linker_lists.each do |card|
-
next unless card.item_type_id != type_id
-
errors.add(:type,
-
"can't be changed because #{name} " \
-
"is referenced by list card #{card.name}")
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/list_changes.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Path)
-
#
-
1
module Path;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/path.rb"; end
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
# Decko uses "path" a bit unusually. In most formats, it returns a full url. In HTML,
-
# it provides everything after the domain/port.
-
#
-
# If you're feeling your saucy oats, you might point out that typically "paths" don't
-
# include queries and fragment identifiers, much less protocols, domains, and ports.
-
# 10 pedantry points to you! But "path" has just four letters and is smart about
-
# format needs, so using it will lead you down the right ... something or other.
-
-
# Format#path is for generating standard card routes, eg, assuming the card
-
# associated with the current format is named "current", it will generate paths like
-
# these:
-
-
# path view: :bar -> "current?view=bar"
-
# path mark: [mycardid] -> "mycardname"
-
# path format: :csv) -> "current.csv"
-
# path action: :update -> "update/current"
-
-
# #path produces paths that follow one of three main patterns:
-
-
# 1. mark[.format][?query] # standard GET request
-
# 2. action/mark[?query] # GET variant of standard actions
-
# 3. new/mark # shortcut for "new" view of cardtype
-
-
# @param opts [Hash, String] a String is treated as a complete path and
-
# bypasses all processing
-
# @option opts [String, Card::Name, Integer, Symbol, Card] :mark
-
# @option opts [Symbol] :action card action (:create, :update, :delete)
-
# @option opts [Symbol] :format
-
# @option opts [Hash] :card
-
# @option opts [TrueClass] :no_mark
-
-
1
CAST_PARAMS = { slot: { hide: :array, show: :array, wrap: :array } }.freeze
-
# TODO: monkey API for this
-
-
1
def path opts={}
-
6055
return opts unless opts.is_a? Hash
-
5052
path = new_cardtype_path(opts) || standard_path(opts)
-
5052
contextualize_path path
-
end
-
-
# in base format (and therefore most other formats), even internal paths
-
# are rendered as absolute urls.
-
1
def contextualize_path relative_path
-
19
card_url relative_path
-
end
-
-
1
private
-
-
1
def new_cardtype_path opts
-
5052
return unless valid_opts_for_new_cardtype_path? opts
-
39
"#{opts.delete :action}/#{path_mark opts}#{path_query opts}"
-
end
-
-
1
def valid_opts_for_new_cardtype_path? opts
-
5052
return unless opts[:action].in? %i[new type]
-
-
# "new" and "type" are not really an action and are only
-
# a valid value here for this path
-
39
opts[:mark].present?
-
end
-
-
1
def standard_path opts
-
5013
path_base(opts) + path_extension(opts) + path_query(opts)
-
end
-
-
1
def path_base opts
-
5013
mark = path_mark opts
-
5013
if (action = path_action opts)
-
611
action_base action, mark
-
else
-
4402
mark
-
end
-
end
-
-
1
def action_base action, mark
-
611
mark.present? ? "#{action}/#{mark}" : "card/#{action}"
-
# the card/ prefix prevents interpreting action as cardname
-
end
-
-
1
def path_action opts
-
5013
return unless (action = opts.delete(:action)&.to_sym)
-
2005
%i[create update delete].find { |a| a == action }
-
end
-
-
1
def path_mark opts
-
5052
return "" if markless_path? opts
-
5001
name = opts[:mark] ? Card::Name[opts.delete(:mark)] : card.name
-
5001
add_unknown_name_to_opts name.to_name, opts
-
5001
name.to_name.url_key
-
end
-
-
1
def markless_path? opts
-
5052
opts[:action] == :create || opts.delete(:no_mark)
-
end
-
-
1
def path_extension opts
-
5013
extension = opts.delete :format
-
5013
extension ? ".#{extension}" : ""
-
end
-
-
1
def path_query opts
-
5052
opts = cast_path_opts opts
-
5052
opts.empty? ? "" : "?#{opts.to_param}"
-
end
-
-
# normalizes certain path opts to specified data types
-
1
def cast_path_opts opts, cast_hash=nil
-
5130
cast_hash ||= CAST_PARAMS
-
5130
return opts unless opts.is_a?(::Hash)
-
5130
opts.each do |key, value|
-
1615
next unless (cast_to = cast_hash[key])
-
156
opts[key] = cast_path_value value, cast_to
-
end
-
end
-
-
1
def cast_path_value value, cast_to
-
156
if cast_to.is_a? Hash
-
78
cast_path_opts value, cast_to
-
else
-
78
send "cast_path_value_as_#{cast_to}", value
-
end
-
end
-
-
1
def cast_path_value_as_array value
-
78
Array.wrap value
-
end
-
-
1
def add_unknown_name_to_opts name, opts
-
5001
return if name_specified?(opts) || name_standardish?(name) || Card.known?(name)
-
13
opts[:card] ||= {}
-
13
opts[:card][:name] = name
-
end
-
-
1
def name_specified? opts
-
5001
opts[:card] && opts[:card][:name]
-
end
-
-
# no name info will be lost by using url_key
-
1
def name_standardish? name
-
5001
name.s == Card::Name.url_key_to_standard(name.url_key)
-
end
-
end
-
-
2
module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
-
1
def add_unknown_name_to_opts name, opts
-
# noop
-
end
-
end
-
-
2
module CssFormat; module_parent.send :register_set_format, Card::Format::CssFormat, self; extend Card::Set::AbstractFormat
-
1
def contextualize_path relative_path
-
if Card.config.file_storage == :local
-
# absolute paths lead to invalid assets path in css for cukes
-
card_path relative_path
-
else
-
# ...but relative paths are problematic when machine output and
-
# hard-coded assets (like fonts) are on different servers
-
card_url relative_path
-
end
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
# in HTML, decko paths rendered as relative to the site's root.
-
1
def contextualize_path relative_path
-
5697
card_path relative_path
-
end
-
end
-
-
2
module EmailHtmlFormat; module_parent.send :register_set_format, Card::Format::EmailHtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def contextualize_path relative_path
-
53
card_url relative_path
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/path.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (RichHtml)
-
#
-
1
module RichHtml;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
delegate :class_up, :class_down, :with_class_up, :without_upped_class, :classy,
-
to: :voo
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module RichHtml;
-
# Set: All cards (RichHtml, Alert)
-
#
-
1
module Alert;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/alert.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
# alert_types: 'success', 'info', 'warning', 'danger'
-
1
def alert alert_type, dismissable=false, disappear=false, args={}
-
53
add_class args, alert_classes(alert_type, dismissable, disappear)
-
53
wrap_with :div, args.merge(role: "alert") do
-
53
[(alert_close_button if dismissable), output(yield)]
-
end
-
end
-
-
1
def alert_classes alert_type, dismissable, disappear
-
53
classes = ["alert", "alert-#{alert_type}"]
-
53
classes << "alert-dismissible " if dismissable
-
53
classes << "_disappear" if disappear
-
53
classy classes
-
end
-
-
1
def alert_close_button
-
51
wrap_with :button, type: "button", "data-dismiss": "alert",
-
class: "close", "aria-label": "Close" do
-
51
wrap_with :span, "×", "aria-hidden" => true
-
end
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/alert.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module RichHtml;
-
# Set: All cards (RichHtml, Content)
-
#
-
1
module Content;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/content.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def prepare_content_slot
-
253
class_up "card-slot", "d0-card-content"
-
253
voo.hide :menu
-
end
-
-
254
before(:content) { prepare_content_slot }
-
-
1
view :content do
-
246
voo.hide :edit_button
-
246
wrap do
-
246
[_render_menu, _render_core, _render_edit_button(edit: :inline)]
-
end
-
end
-
-
1
before(:content_with_edit_button) do
-
prepare_content_slot
-
end
-
-
1
view :content_with_edit_button do
-
wrap do
-
[_render_menu, _render_core, _render_edit_button(edit: :inline)]
-
end
-
end
-
-
1
view :short_content, wrap: { div: { class: "text-muted" } } do
-
48
short_content
-
end
-
-
1
view :raw_one_line_content, unknown: :mini_unknown,
-
wrap: { div: { class: "text-muted" } } do
-
raw_one_line_content
-
end
-
-
1
view :one_line_content, unknown: :mini_unknown,
-
wrap: { div: { class: "text-muted" } } do
-
one_line_content
-
end
-
-
1
before(:content_with_title) { prepare_content_slot }
-
-
1
view :content_with_title do
-
wrap true, title: card.format(:text).render_core do
-
[_render_menu, _render_core]
-
end
-
end
-
-
1
before :content_panel do
-
prepare_content_slot
-
class_up "card-slot", "card"
-
end
-
-
1
view :content_panel do
-
wrap do
-
wrap_with :div, class: "card-body" do
-
[_render_menu, _render_core]
-
end
-
end
-
end
-
-
1
view :titled do
-
248
@content_body = true
-
248
wrap do
-
[
-
496
naming { render_header },
-
render_flash,
-
248
wrap_body { render_titled_content },
-
render_comment_box(optional: :hide)
-
]
-
end
-
end
-
-
1
view :labeled, unknown: true do
-
6
@content_body = true
-
6
wrap(true, class: "row") do
-
12
labeled(render_title, wrap_body { "#{render_menu}#{render_labeled_content}" } )
-
end
-
end
-
-
1
def labeled label, content
-
6
haml :labeled, label: label, content: content
-
end
-
-
1
def labeled_field field, item_view=:name, opts={}
-
opts[:title] ||= Card.fetch_name field
-
field_nest field, opts.merge(view: :labeled,
-
items: (opts[:items] || {}).merge(view: item_view))
-
end
-
-
1
view :open do
-
toggle_logic
-
@toggle_mode = :open
-
@content_body = true
-
frame do
-
[_render_open_content, render_comment_box(optional: :hide)]
-
end
-
end
-
-
1
view :closed do
-
with_nest_mode :compact do
-
toggle_logic
-
class_up "d0-card-body", "closed-content"
-
@content_body = false
-
@toggle_mode = :close
-
frame
-
end
-
end
-
-
1
def toggle_logic
-
show_view?(:title_link, :hide) ? voo.show(:icon_toggle) : voo.show(:title_toggle)
-
end
-
-
1
def current_set_card
-
set_name = params[:current_set]
-
set_name ||= "#{card.name}+*type" if card.known? && card.type_id == Card::CardtypeID
-
set_name ||= "#{card.name}+*self"
-
Card.fetch(set_name)
-
end
-
-
1
def raw_one_line_content
-
cleaned = Card::Content.clean! render_raw, {}
-
cut_with_ellipsis cleaned
-
end
-
-
1
def one_line_content
-
# TODO: use a version of Card::Content.smart_truncate
-
# that counts characters instead of clean!
-
cleaned = Card::Content.clean! render_core, {}
-
cut_with_ellipsis cleaned
-
end
-
-
# LOCALIZE
-
1
def short_content
-
42
short_content_items || short_content_fields || short_content_from_core
-
end
-
-
1
def short_content_items
-
42
return unless card.respond_to? :count
-
7
"#{count} #{'item'.pluralize count}"
-
end
-
-
1
def short_content_fields
-
35
with_short_content_fields do |num_fields|
-
6
"#{num_fields} #{'field'.pluralize num_fields}" if num_fields.positive?
-
end
-
end
-
-
1
def with_short_content_fields
-
35
yield nested_field_names.size if voo.structure || card.structure
-
end
-
-
1
def short_content_from_core
-
29
content = render_core
-
29
if content.blank?
-
12
"empty"
-
17
elsif content.size <= 5
-
2
content
-
15
elsif content.count("\n") < 2
-
14
"#{content.size} characters"
-
else
-
1
"#{content.count("\n") + 1} lines"
-
end
-
end
-
-
1
def count
-
15
@count ||= card.count
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/content.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module RichHtml;
-
# Set: All cards (RichHtml, Error)
-
#
-
1
module Error;
-
1
extend Card::Set
-
2
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/error.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :server_error, template: :haml
-
-
1
view :debug_server_error, wrap: { modal: { size: :full } } do
-
error_page = BetterErrors::ErrorPage.new Card::Error.current,
-
"PATH_INFO" => request.env["REQUEST_URI"]
-
haml :debug_server_error, {}, error_page
-
end
-
-
1
view :unknown do
-
75
createable { wrap { unknown_link "#{unknown_icon} #{render_title}" } }
-
end
-
-
# icon only, no wrap
-
1
view :mini_unknown, unknown: true, cache: :never do
-
createable { unknown_link unknown_icon }
-
end
-
-
1
def createable
-
25
card.ok?(:create) ? yield : ""
-
end
-
-
1
def unknown_link text
-
25
path_opts = voo.type ? { card: { type: voo.type } } : {}
-
25
link_to_view :new_in_modal, text, path: path_opts, class: classy("unknown-link")
-
end
-
-
1
def unknown_icon
-
25
fa_icon "plus-square"
-
end
-
-
1
view :compact_missing, perms: :none do
-
wrap_with :span, h(title_in_context), class: "text-muted"
-
end
-
-
1
view :conflict, cache: :never do
-
2
actor_link = link_to_card card.last_action.act.actor.name
-
2
class_up "card-slot", "error-view"
-
2
wrap do # LOCALIZE
-
2
alert "warning" do
-
2
%(
-
<strong>Conflict!</strong>
-
<span class="new-current-revision-id">#{card.last_action_id}</span>
-
<div>#{actor_link} has also been making changes.</div>
-
<div>Please examine below, resolve above, and re-submit.</div>
-
#{render_act}
-
)
-
end
-
end
-
end
-
-
1
view :errors, perms: :none do
-
1
return if card.errors.empty?
-
-
1
voo.title = card.name.blank? ? "Problems" : tr(:problems_name, cardname: card.name)
-
1
voo.hide! :menu
-
1
class_up "alert", "card-error-msg"
-
1
standard_errors voo.title
-
end
-
-
1
view :not_found do
-
voo.hide! :menu
-
voo.title = "Not Found"
-
frame do
-
[not_found_errors, sign_in_or_up_links("to create it")]
-
end
-
end
-
-
1
view :denial do
-
focal? ? loud_denial : quiet_denial
-
end
-
-
1
def view_for_unknown view
-
29
main? && ok?(:create) ? :new : super
-
end
-
-
1
def show_all_errors?
-
# make configurable by env
-
Auth.always_ok? || Rails.env.development?
-
end
-
-
1
def error_cardname exception
-
cardname = super
-
show_all_errors? ? backtrace_link(cardname, exception) : cardname
-
end
-
-
1
def rendering_error exception, view
-
wrap_with(:span, class: "render-error alert alert-danger") { super }
-
end
-
-
1
def error_modal_id
-
@error_modal_id ||= unique_id
-
end
-
-
1
def error_message exception
-
%{
-
<h3>Error message (visible to admin only)</h3>
-
<p><strong>#{CGI.escapeHTML exception.message}</strong></p>
-
<div>#{exception.backtrace * "<br>\n"}</div>
-
}
-
end
-
-
1
def backtrace_link cardname, exception
-
# TODO: make this a modal link after new modal handling is merged in
-
wrap_with(:span, title: error_message(exception)) { cardname }
-
end
-
-
1
def standard_errors heading=nil
-
1
alert "warning", true do
-
[
-
1
(wrap_with(:h4, heading, class: "alert-heading error") if heading),
-
error_messages.join("<hr>")
-
]
-
end
-
end
-
-
1
def error_messages
-
1
card.errors.map do |attrib, msg|
-
1
attrib == :abort ? h(msg) : standard_error_message(attrib, msg)
-
end
-
end
-
-
1
def standard_error_message attribute, message
-
1
"<p><strong>#{h attribute.to_s.upcase}:</strong> #{h message}</p>"
-
end
-
-
1
def not_found_errors
-
if card.errors.any?
-
standard_errors
-
else
-
haml :not_found
-
end
-
end
-
-
1
def sign_in_or_up_links to_task
-
return if Auth.signed_in?
-
-
links = [signin_link, signup_link].compact.join " #{tr :or} "
-
wrap_with(:div) do
-
[tr(:please), links, to_task].join(" ") + "."
-
end
-
end
-
-
1
def signin_link
-
link_to_card :signin, tr(:sign_in),
-
class: "signin-link", slotter: true, path: { view: :open }
-
end
-
-
1
def signup_link
-
return unless signup_ok?
-
-
link_to_card :signup, tr(:sign_up),
-
class: "signup-link", slotter: true, path: { action: :new }
-
end
-
-
1
def signup_ok?
-
Card.new(type_id: Card::SignupID).ok? :create
-
end
-
-
1
def quiet_denial
-
wrap_with :span, class: "denied" do
-
"<!-- Sorry, you don't have permission (#{@denied_task}) -->"
-
end
-
end
-
-
1
def loud_denial
-
voo.hide :menu
-
frame do
-
[wrap_with(:h1, tr(:sorry)),
-
wrap_with(:div, loud_denial_message)]
-
end
-
end
-
-
1
def loud_denial_message
-
to_task = @denied_task ? tr(:denied_task, denied_task: @denied_task) : tr(:to_do_that)
-
-
case
-
when not_denied_task_read?
-
tr(:read_only)
-
when Auth.signed_in?
-
tr(:need_permission_task, task: to_task)
-
else
-
Env.save_interrupted_action request.env["REQUEST_URI"]
-
sign_in_or_up_links to_do_unauthorized_task
-
end
-
end
-
-
1
def not_denied_task_read?
-
@denied_task != :read && Card.config.read_only
-
end
-
-
1
def to_do_unauthorized_task
-
@denied_task ? tr(:denied_task, denied_task: @denied_task) : tr(:to_do_that)
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/error.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module RichHtml;
-
# Set: All cards (RichHtml, Frame)
-
#
-
1
module Frame;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/frame.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :flash, cache: :never, unknown: true, perms: :none do
-
300
flash_notice = params[:flash] || Env.success.flash
-
300
return "" unless flash_notice.present? && focal?
-
-
Array(flash_notice).join "\n"
-
end
-
-
1
def frame &block
-
52
standard_frame &block
-
end
-
-
1
def standard_frame slot=true
-
52
with_frame slot do
-
104
wrap_body { yield } if block_given?
-
end
-
end
-
-
1
def with_frame slot=true, header=frame_header, slot_opts={}
-
52
voo.hide :help
-
52
add_name_context
-
52
wrap slot, slot_opts do
-
52
panel do
-
52
[header, frame_help, render_flash, (yield if block_given?)]
-
end
-
end
-
end
-
-
1
def frame_header
-
52
_render_header
-
end
-
-
1
def frame_help
-
74
with_class_up "help-text", "alert alert-info" do
-
74
_render :help
-
end
-
end
-
-
1
def frame_and_form action, form_opts={}
-
51
form_opts ||= {}
-
51
frame do
-
51
card_form action, form_opts do
-
51
yield
-
end
-
end
-
end
-
-
1
def panel
-
52
wrap_with :div, class: classy("d0-card-frame") do
-
52
yield
-
end
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/frame.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module RichHtml;
-
# Set: All cards (RichHtml, Header)
-
#
-
1
module Header;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/header.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :header, perms: :none do
-
300
main_header
-
end
-
-
1
def main_header
-
300
header_wrap _render_header_title
-
end
-
-
1
def header_wrap content=nil
-
300
haml :header_wrap, content: (block_given? ? yield : output(content))
-
end
-
-
1
view :header_title, perms: :none do
-
300
header_title_elements
-
end
-
-
1
def header_title_elements
-
300
voo.hide :title_toggle if show_view?(:icon_toggle, :hide)
-
300
title_view = show_view?(:title_toggle, :hide) ? :title_toggle : :title
-
300
[_render_icon_toggle(optional: :hide), _render(title_view)]
-
end
-
-
1
def show_draft_link?
-
card.drafts.present? && @slot_view == :edit
-
end
-
-
1
view :title_toggle, perms: :none do
-
content_toggle(_render_title(hide: :title_link))
-
end
-
-
1
view :icon_toggle, perms: :none do
-
direction = @toggle_mode == :close ? :expand : :collapse_down
-
content_toggle icon_tag(direction)
-
end
-
-
1
view :toggle, :icon_toggle # deprecated; use icon_toggle
-
-
1
def content_toggle text=""
-
return if text.nil?
-
-
verb, adjective = toggle_verb_adjective
-
link_to_view adjective, text, title: "#{verb} #{card.name}", # LOCALIZE
-
class: "toggle-#{adjective} toggler nodblclick"
-
end
-
-
1
def toggle_view
-
300
toggle_verb_adjective.last
-
end
-
-
1
TOGGLE_MAP = { close: %w[open open], open: %w[close closed] } # LOCALIZE first item
-
-
1
def toggle_verb_adjective
-
300
TOGGLE_MAP[@toggle_mode || :open] ||
-
raise(Card::Error, "invalid toggle mode: #{@toggle_mode}")
-
end
-
-
1
def structure_editable?
-
card.structure && card.template.ok?(:update)
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/header.rb ~~
-
# -*- encoding : utf-8 -*-
-
5
class Card; module Set; class All; module RichHtml;; module HtmlViews;
-
# Set: All cards (RichHtml, HtmlViews, Guide)
-
#
-
1
module Guide;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/html_views/guide.rb"; end
-
1
def guide_card
-
100
guide_card = rule_card(:guide)
-
100
return unless guide_card
-
-
100
guide_card = guide_card.first_card if guide_card.type_id == Card::PointerID
-
100
guide_card if guide_card.ok?(:read)
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :guide, unknown: true, cache: :never, wrap: :slot do
-
guide
-
end
-
-
1
def guide
-
50
guide_text = rule_based_guide
-
50
return "" unless guide_text.present?
-
-
50
if (rule_card = card.help_rule_card)
-
edit_link = with_nest_mode(:normal) { nest(rule_card, view: :edit_link) }
-
guide_text = "<span class='d-none'>#{edit_link}</span>#{guide_text}"
-
end
-
50
wrap_with :div, guide_text, class: classy("guide-text")
-
end
-
-
1
def alert_guide
-
50
guide_text = guide
-
50
return "" unless guide_text.present?
-
-
100
alert(:secondary, true, false, class: "guide") { guide_text }
-
end
-
-
1
def raw_guide_text
-
50
false
-
end
-
-
1
def rule_based_guide
-
50
if raw_guide_text
-
with_nest_mode :normal do
-
process_content raw_guide_text, chunk_list: :references
-
# render guide text with current card's format
-
# so current card's context is used in guide card nests
-
end
-
50
elsif card.guide_card
-
50
with_nest_mode :normal do
-
50
nest card.guide_card, view: :core
-
end
-
else
-
""
-
end
-
end
-
end
-
end;end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/html_views/guide.rb ~~
-
# -*- encoding : utf-8 -*-
-
5
class Card; module Set; class All; module RichHtml;; module HtmlViews;
-
# Set: All cards (RichHtml, HtmlViews, Help)
-
#
-
1
module Help;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/html_views/help.rb"; end
-
1
def help_rule_card
-
831
help_card = rule_card(:help)
-
831
help_card if help_card&.ok?(:read)
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :help, unknown: true, cache: :never, wrap: :slot do
-
286
help = help_text
-
286
return "" unless help.present?
-
-
14
wrap_with :div, wrap_help_text(help), class: classy("help-text")
-
end
-
-
1
view :help_text, unknown: true, cache: :never do
-
236
wrap_help_text help_text
-
end
-
-
1
def wrap_help_text text
-
269
help = text
-
269
if (rule_card = card.help_rule_card)
-
4
edit_link = with_nest_mode(:normal) { nest(rule_card, view: :edit_link) }
-
2
help = "<span class='d-none'>#{edit_link}</span>#{text}"
-
end
-
269
help
-
end
-
-
1
view :lead do
-
class_up "card-slot", "lead"
-
_view_content
-
end
-
-
1
def help_text
-
522
voo.help || rule_based_help
-
end
-
-
1
def raw_help_text
-
534
card.try(:raw_help_text) || card.help_rule_card&.content
-
end
-
-
1
def rule_based_help
-
540
return "" unless (help_text = raw_help_text)
-
-
30
with_nest_mode :normal do
-
30
process_content help_text, chunk_list: :references
-
# render help card with current card's format
-
# so current card's context is used in help card nests
-
end
-
end
-
end
-
end;end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/html_views/help.rb ~~
-
# -*- encoding : utf-8 -*-
-
5
class Card; module Set; class All; module RichHtml;; module HtmlViews;
-
# Set: All cards (RichHtml, HtmlViews, Info)
-
#
-
1
module Info;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/html_views/info.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :type, unknown: true do
-
248
link_to_card card.type_card, nil, class: "cardtype"
-
end
-
-
1
view :change do
-
voo.show :title_link
-
voo.hide :menu
-
wrap do
-
[_render_title,
-
_render_menu,
-
_render_last_action]
-
end
-
end
-
-
1
view :last_action do
-
act = card.last_act
-
return unless act
-
-
action = act.action_on card.id
-
return unless action
-
-
action_verb =
-
case action.action_type
-
when :create then "added"
-
when :delete then "deleted"
-
else
-
link_to_view :history, "edited", class: "last-edited", rel: "nofollow"
-
end
-
-
%(
-
<span class="last-update">
-
#{action_verb} #{_render_acted_at} ago by
-
#{subformat(card.last_actor)._render_link}
-
</span>
-
)
-
end
-
-
1
view :type_info do
-
return unless card.type_code != :basic
-
-
wrap_with :span, class: "type-info float-right" do
-
link_to_card card.type_name, nil, class: "navbar-link"
-
end
-
end
-
-
1
view :view_list do
-
%i[bar box info_bar open closed titled labeled content content_panel].map do |v|
-
wrap_with :p, [content_tag(:h3, v), render(v, show: :menu)]
-
end.flatten.join ""
-
end
-
-
1
view :demo do
-
frame do
-
[
-
view_select,
-
wrap_with(:div, view_demo, class: "demo-slot")
-
]
-
end
-
end
-
-
1
def demo_view
-
Env.params[:demo_view] || :core
-
end
-
-
1
def view_demo
-
wrap(true) do
-
render demo_view
-
end
-
end
-
-
1
def view_select
-
card_form :get, success: { view: :demo } do
-
select_tag :demo_view, options_for_select(all_views, demo_view),
-
class: "_submit-on-select"
-
end
-
end
-
-
1
def all_views
-
Card::Set::Format::AbstractFormat::ViewDefinition.views
-
.slice(*self.class.ancestors)
-
.values.map(&:keys).flatten.uniq
-
end
-
end
-
end;end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/html_views/info.rb ~~
-
# -*- encoding : utf-8 -*-
-
5
class Card; module Set; class All; module RichHtml;; module HtmlViews;
-
# Set: All cards (RichHtml, HtmlViews, Size)
-
#
-
1
module Size;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/html_views/size.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
SIZE_IN_PX = { icon: 16, small: 75, medium: 200, large: 500 }.freeze
-
-
# used to control size of svg
-
1
view :max_size do
-
if voo.size.is_a?(String) && voo.size.match(/^\d+x\d+\$/)
-
max_size(*voo.size.split("x"))
-
else
-
px = SIZE_IN_PX[voo.size&.to_sym] || 200
-
max_size px, px
-
end
-
end
-
-
1
def max_size w, h
-
"max-width: #{w}px; max-height: #{h}px"
-
end
-
end
-
end;end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/html_views/size.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module RichHtml;
-
# Set: All cards (RichHtml, Menu)
-
#
-
1
module Menu;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/menu.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :menu, denial: :blank, unknown: true do
-
305
return "" if card.unknown?
-
-
255
wrap_with :div, class: "card-menu #{menu_link_classes}" do
-
255
[render_help_link,
-
menu_link,
-
255
(voo.show?(:bridge_link) ? bridge_link(false) : nil)]
-
end
-
end
-
-
1
def menu_link
-
255
case voo.edit
-
when :inline
-
edit_inline_link
-
when :full
-
edit_in_bridge_link
-
else # :standard
-
255
edit_link
-
end
-
end
-
-
1
def edit_view
-
case voo.edit
-
when :inline
-
:edit_inline
-
when :full
-
:edit
-
else # :standard
-
edit_link
-
end
-
end
-
-
1
view :edit_link, unknown: true, denial: :blank do
-
2
edit_link edit_link_view
-
end
-
-
1
def edit_link_view
-
:edit
-
end
-
-
1
view :full_page_link do
-
full_page_link
-
end
-
-
1
view :bridge_link, unknown: true do
-
28
bridge_link
-
end
-
-
1
def bridge_link in_modal=true
-
283
opts = { class: "bridge-link" }
-
283
if in_modal
-
# add_class opts, "close"
-
28
opts["data-slotter-mode"] = "modal-replace"
-
end
-
283
link_to_view :bridge, material_icon(:more_horiz), opts
-
end
-
-
# no caching because help_text view doesn't cache, and we can't have a
-
# stub in the data-content attribute or it will get html escaped.
-
1
view :help_link, cache: :never, unknown: true do
-
255
help_link render_help_text, help_title
-
end
-
-
1
def help_link text=nil, title=nil
-
255
opts = help_popover_opts text, title
-
255
add_class opts, "_card-menu-popover"
-
255
link_to help_icon, opts
-
end
-
-
1
def help_popover_opts text=nil, title=nil
-
255
text ||= render_help_text
-
255
opts = { "data-placement": :left, class: "help-link" }
-
255
popover_opts text, title, opts
-
end
-
-
1
def help_icon
-
255
material_icon("help")
-
end
-
-
1
def help_title
-
255
"#{name_parts_links} (#{render_type}) #{full_page_link unless card.simple?}"
-
end
-
-
1
def name_parts_links
-
255
card.name.parts.map do |part|
-
391
link_to_card part
-
end.join Card::Name.joint
-
end
-
-
1
def full_page_link
-
109
link_to_card full_page_card, full_page_icon, class: classy("full-page-link")
-
end
-
-
1
def full_page_card
-
109
card
-
end
-
-
1
def edit_in_bridge_link opts={}
-
edit_link :bridge, opts
-
end
-
-
1
def edit_link view=:edit, opts={}
-
257
link_to_view view, opts.delete(:link_text) || menu_icon,
-
edit_link_opts(opts.reverse_merge(modal: :lg))
-
end
-
-
# @param modal [Symbol] modal size
-
1
def edit_link_opts modal: nil
-
257
opts = { class: classy("edit-link") }
-
257
if modal
-
257
opts[:"data-slotter-mode"] = "modal"
-
257
opts[:"data-modal-class"] = "modal-#{modal}"
-
end
-
257
opts
-
end
-
-
1
def menu_link_classes
-
255
"nodblclick" + (show_view?(:hover_link) ? " _show-on-hover" : "")
-
end
-
-
1
def menu_icon
-
279
material_icon "edit"
-
end
-
-
1
def full_page_icon
-
109
icon_tag :open_in_new
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/menu.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module RichHtml;
-
# Set: All cards (RichHtml, Modal)
-
#
-
1
module Modal;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/modal.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
MODAL_SIZE = { small: "sm", medium: nil, large: "lg", full: "full" }.freeze
-
1
MODAL_CLOSE_OPTS = { "data-dismiss": "modal",
-
"data-cy": "close-modal" }.freeze
-
-
1
wrapper :modal do |opts={}|
-
22
haml :modal_dialog, body: interior,
-
classes: modal_dialog_classes(opts),
-
title: normalize_modal_option(:title, opts),
-
menu: normalize_modal_option(:menu, opts),
-
footer: normalize_modal_option(:footer, opts)
-
end
-
-
1
def normalize_modal_option key, opts
-
66
val = opts[key]
-
66
return render("modal_#{key}") unless val
-
66
cast_model_option val
-
end
-
-
1
def cast_model_option val
-
95
case val
-
when Symbol
-
66
cast_model_option_symbol val
-
when Proc
-
val.call(self)
-
else
-
29
val
-
end
-
end
-
-
1
def cast_model_option_symbol val
-
66
respond_to?(val) ? send(val) : val
-
end
-
-
1
view :modal, wrap: :modal do
-
""
-
end
-
-
1
def show_in_modal_link link_text, body
-
link_to_view :modal, link_text, "data-modal-body": body, "data-slotter-mode": "modal"
-
end
-
-
1
def modal_close_button link_text="Close", opts={}
-
21
classes = opts.delete(:class)
-
21
button_opts = opts.merge(MODAL_CLOSE_OPTS)
-
21
add_class button_opts, classes if classes
-
21
button_tag link_text, button_opts
-
end
-
-
1
def modal_submit_button opts={}
-
add_class opts, "submit-button _close-modal"
-
submit_button opts
-
end
-
-
1
view :modal_menu, unknown: true, wrap: :modal_menu do
-
[close_modal_window, pop_out_modal_window]
-
end
-
-
1
wrapper :modal_menu, :div, class: "modal-menu ml-auto"
-
-
1
view :modal_title, unknown: true do
-
""
-
end
-
-
1
view :modal_footer, unknown: true do
-
button_tag "Close",
-
class: "btn-xs _close-modal float-right",
-
"data-dismiss" => "modal"
-
end
-
-
1
view :modal_link do
-
modal_link _render_title, size: voo.size
-
end
-
-
1
def modal_link text=nil, opts={}
-
opts = modal_link_opts(opts)
-
opts[:path][:layout] ||= :modal
-
link_to text, opts
-
end
-
-
1
def modal_link_opts opts
-
7
add_class opts, "slotter"
-
7
opts.reverse_merge! path: {},
-
"data-slotter-mode": "modal",
-
"data-modal-class": modal_dialog_classes(opts),
-
remote: true
-
7
opts
-
end
-
-
1
def modal_dialog_classes opts
-
29
classes = [classy("modal-dialog")]
-
29
return classes unless opts.present?
-
-
29
add_modal_size_class classes, opts[:size]
-
29
classes << "modal-dialog-centered" if opts[:vertically_centered]
-
29
classes.join " "
-
end
-
-
1
def add_modal_size_class classes, size
-
29
size = normalize_modal_size_class size
-
29
return if size == :medium || size.blank?
-
-
22
classes << "modal-#{MODAL_SIZE[size]}"
-
end
-
-
1
def normalize_modal_size_class size
-
29
size.in?(MODAL_SIZE.keys) ? size : cast_model_option(size)
-
end
-
-
1
def close_modal_window
-
22
link_to icon_tag(:close), path: "",
-
class: "_close-modal close",
-
"data-dismiss": "modal"
-
end
-
-
1
def pop_out_modal_window
-
link_to icon_tag(:new_window), path: {}, class: "pop-out-modal close"
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/modal.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module RichHtml;
-
# Set: All cards (RichHtml, Overlay)
-
#
-
1
module Overlay;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/overlay.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
OVERLAY_CLOSE_OPTS = { class: "_close-overlay btn-sm",
-
"data-dismiss": "overlay",
-
type: "button" }.freeze
-
-
1
wrapper :overlay do |opts|
-
class_up "card-slot", "_overlay d0-card-overlay bg-body"
-
@content_body = true
-
voo.hide! :menu
-
overlay_frame true, overlay_header(opts[:title]), opts[:slot] do
-
interior
-
end
-
end
-
-
1
view :overlay_header, unknown: true do
-
overlay_header
-
end
-
-
1
view :overlay_title do
-
_render_title
-
end
-
-
1
view :overlay_menu do
-
wrap_with :div, class: "btn-group btn-group-sm align-self-start ml-auto" do
-
[render_overlay_help_link, slotify_overlay_link, close_overlay_link]
-
end
-
end
-
-
1
view :overlay_help_link, cache: :never, unknown: true do
-
opts = help_popover_opts
-
add_open_guide_opts opts
-
overlay_menu_link "question-circle", opts
-
end
-
-
1
def add_open_guide_opts opts
-
return unless card.guide_card
-
-
slot_selector = ".bridge-sidebar > ._overlay-container-placeholder > .card-slot"
-
opts.merge! remote: true,
-
href: path(mark: card, view: :overlay_guide),
-
"data-slot-selector": slot_selector,
-
"data-slotter-mode": "overlay"
-
add_class opts, "slotter"
-
end
-
-
1
def slotify_overlay_link
-
overlay_menu_link "external-link-square", card: card
-
end
-
-
1
def close_overlay_link
-
overlay_menu_link :close, path: "#", "data-dismiss": "overlay"
-
end
-
-
1
def overlay_close_button link_text="Close", opts={}
-
classes = opts.delete(:class)
-
button_opts = opts.merge(OVERLAY_CLOSE_OPTS)
-
add_class button_opts, classes if classes
-
button_tag link_text, button_opts
-
end
-
-
1
def overlay_delete_button
-
opts = { no_success: true }.merge OVERLAY_CLOSE_OPTS
-
delete_button opts
-
end
-
-
1
def overlay_save_and_close_button
-
submit_button text: "Save and Close", class: "_close-on-success",
-
"data-cy": "submit-overlay"
-
end
-
-
1
def overlay_menu_link icon, args={}
-
add_class args, "border-light text-dark p-1 ml-1"
-
button_link fa_icon(icon, class: "fa-lg"), args.merge(btn_type: "outline-secondary")
-
end
-
-
1
def overlay_header title=nil
-
title ||= _render_overlay_title
-
class_up "d0-card-header", "bg-body"
-
class_up "d0-card-header-title", "d-flex"
-
header_wrap [title, _render_overlay_menu]
-
end
-
-
1
def overlay_frame slot=true, header=render_overlay_header, slot_opts=nil
-
slot_opts ||= {}
-
overlay_framer slot, header, slot_opts do
-
wrap_body { yield }
-
end
-
end
-
-
1
def haml_overlay_frame slot=true, header=render_overlay_header
-
overlay_framer slot, header, {} do
-
haml_wrap_body { yield }
-
end
-
end
-
-
1
private
-
-
1
def overlay_framer slot, header, slot_opts
-
class_up "card-slot", "_overlay"
-
with_frame slot, header, slot_opts do
-
yield
-
end
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/overlay.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module RichHtml;
-
# Set: All cards (RichHtml, ProcessLayout)
-
#
-
1
module ProcessLayout;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/process_layout.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
# TODO: use CodeFile cards for these
-
# builtin layouts allow for rescue / testing
-
# HTML_LAYOUTS = Mod::Loader.load_layouts(:html).merge "none" => "{{_main}}"
-
# HAML_LAYOUTS = Mod::Loader.load_layouts(:haml)
-
-
1
def show_with_page_layout view, args
-
224
main!
-
224
args = main_render_args view, args
-
224
if explicit_modal_wrapper?(view) && page_layout.to_sym != :modal
-
21
render_outside_of_layout view, args
-
else
-
203
render_with_layout view, page_layout, args
-
end
-
end
-
-
1
def main_render_args view, args
-
224
args[:view] = view if view
-
224
args[:main] = true
-
224
args[:main_view] = true
-
224
args
-
end
-
-
1
def page_layout
-
245
params[:layout] || layout_name_from_rule || :default
-
end
-
-
1
def render_with_layout view, layout, args={}
-
224
view_opts = Layout.main_nest_opts(layout, self)
-
224
view ||= view_opts.delete(:view) || default_nest_view
-
224
view_opts[:home_view] = view
-
224
view_opts[:layout] = layout
-
224
render! view, view_opts.reverse_merge(args)
-
end
-
-
1
def render_outside_of_layout view, args
-
21
body = render_with_layout(nil, page_layout, {})
-
21
modal = render!(view, args)
-
21
if body.include?("</body>")
-
# a bit hacky
-
# the problem is that the body tag has to be in the layout
-
# so that you can add layout css classes like <body class="right-sidebar">
-
21
body.sub!("</body>", "#{modal}</body>")
-
else
-
body += modal
-
end
-
21
body
-
end
-
-
1
def show_layout?
-
845
!Env.ajax? || params[:layout]
-
end
-
-
1
def explicit_modal_wrapper? view
-
224
return unless view_setting(:wrap, view)
-
-
42
wrapper_names(view_setting(:wrap, view)).any? { |n| n == :modal || n == :bridge }
-
end
-
-
1
def wrapper_names wrappers
-
21
case wrappers
-
21
when Hash then wrappers.keys
-
when Array then wrappers.map { |w| w.is_a?(Array) ? w.first : w }
-
else [wrappers]
-
end
-
end
-
-
1
def layout_name_from_rule
-
245
card.rule_card(:layout)&.try :item_name
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/process_layout.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module RichHtml;
-
# Set: All cards (RichHtml, Show)
-
#
-
1
module Show;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/show.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def show view, args
-
274
content = send show_method, view, args
-
274
show_full_page? ? wrap_with_html_page(content) : content
-
-
# TODO: remove the following after tracking down wikirate encoding bug
-
rescue Card::Error::ServerError => e
-
if e.message.match?(/invalid byte sequence/)
-
Card::Lexicon.cache.reset
-
Rails.logger.info "reset name cache to prevent encoding freakiness"
-
end
-
raise e
-
end
-
-
1
def show_method
-
274
"show_#{show_layout? ? :with : :without}_page_layout"
-
end
-
-
1
def show_without_page_layout view, args
-
50
@main = true if params[:is_main] || args[:main]
-
50
args.delete(:layout)
-
50
view ||= args[:home_view] || :open # default_nest_view
-
50
render! view, args
-
end
-
-
1
def show_full_page?
-
274
!Env.ajax?
-
end
-
-
1
wrapper :html_page do
-
224
<<-HTML.strip_heredoc
-
<!DOCTYPE HTML>
-
<html class="h-100">
-
<head>
-
#{head_content}
-
</head>
-
#{interior}
-
</html>
-
HTML
-
end
-
-
1
def head_content
-
224
nest card.rule_card(:head), view: :head_content
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/show.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module RichHtml;
-
# Set: All cards (RichHtml, Title)
-
#
-
1
module Title;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/title.rb"; end
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
view :title, compact: true, perms: :none do
-
standard_title
-
end
-
-
1
def standard_title
-
1683
name_variant title_in_context(voo.title)
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :title do
-
1683
show_view?(:title_link, :hide) ? render_title_link : render_title_no_link
-
end
-
-
1
view :title_link, compact: true, perms: :none do
-
link_to_card card.name, render_title_no_link
-
end
-
-
1
view :title_no_link, compact: true, perms: :none do
-
1683
wrapped_title standard_title
-
end
-
-
1
def title_with_link link_text
-
link_to_card card.name, link_text
-
end
-
-
1
def safe_name
-
253
h super
-
end
-
-
1
def title_in_context title=nil
-
1686
title = title&.html_safe
-
# escape titles generated from card names, but not those set explicitly
-
1686
h super(title)
-
end
-
-
1
def wrapped_title title
-
1683
wrap_with :span, class: classy("card-title"), title: title do
-
1683
title.to_name.parts.join wrapped_joint
-
end
-
end
-
-
1
def wrapped_joint
-
1683
wrap_with :span, "+", classy("joint")
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/title.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module RichHtml;
-
# Set: All cards (RichHtml, Wrapper)
-
#
-
1
module Wrapper;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/wrapper.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
# Does two main things:
-
# (1) gives CSS classes for styling and
-
# (2) adds card data for javascript - including the "card-slot" class,
-
# which in principle is not supposed to be in styles
-
1
def wrap slot=true, slot_attr={}, tag=:div, &block
-
702
method_wrap :wrap_with, tag, slot, slot_attr, &block
-
end
-
-
1
wrapper :slot do |opts|
-
498
class_up "card-slot", opts[:class] if opts[:class]
-
498
method_wrap :wrap_with, :div, true, opts do
-
498
interior
-
end
-
end
-
-
1
def haml_wrap slot=true, slot_attr={}, tag=:div, &block
-
method_wrap :haml_tag, tag, slot, slot_attr, &block
-
end
-
-
1
def method_wrap method, tag, slot, slot_attr, &block
-
1200
@slot_view = @current_view
-
1200
debug_slot do
-
1200
send method, tag, slot_attributes(slot, slot_attr), &block
-
end
-
end
-
-
1
def slot_attributes slot, slot_attr
-
1200
{ id: slot_id, class: wrap_classes(slot), data: wrap_data }.tap do |hash|
-
1200
add_class hash, slot_attr.delete(:class)
-
1200
hash.deep_merge! slot_attr
-
end
-
end
-
-
1
def slot_id
-
1200
"#{card.name.safe_key}-#{@current_view}-view"
-
end
-
-
1
def wrap_data slot=true
-
1200
with_slot_data slot do
-
1200
{ "card-id": card.id, "card-name": h(card.name),
-
"slot-id": SecureRandom.hex(10) }
-
end
-
end
-
-
1
def with_slot_data slot
-
1200
hash = yield
-
# rails helper convert slot hash to json
-
# but haml joins nested keys with a dash
-
1200
hash[:slot] = slot_options_json if slot
-
1200
hash
-
end
-
-
1
def slot_options_json
-
1200
html_escape_except_quotes JSON(slot_options)
-
end
-
-
1
def slot_options
-
1200
options = voo ? voo.slot_options : {}
-
1200
name_context_slot_option options
-
1200
options
-
end
-
-
1
def name_context_slot_option opts
-
1200
return unless initial_context_names.present?
-
-
311
opts[:name_context] = initial_context_names.map(&:key) * ","
-
end
-
-
1
def debug_slot
-
1200
debug_slot? ? debug_slot_wrap { yield } : yield
-
end
-
-
1
def debug_slot?
-
1200
params[:debug] == "slot"
-
end
-
-
1
def debug_slot_wrap
-
pre = "<!--\n\n#{' ' * depth}"
-
post = " SLOT: #{h card.name}\n\n-->"
-
[pre, "BEGIN", post, yield, pre, "END", post].join
-
end
-
-
1
def wrap_classes slot
-
1200
list = slot ? ["card-slot"] : []
-
1200
list += ["#{@current_view}-view", card.safe_set_keys]
-
1200
list << "STRUCTURE-#{voo.structure.to_name.key}" if voo&.structure
-
1200
classy list
-
end
-
-
1
def wrap_body
-
612
wrap_with(:div, class: body_css_classes) { yield }
-
end
-
-
1
def haml_wrap_body
-
wrap_body do
-
capture_haml { yield }
-
end
-
end
-
-
1
def body_css_classes
-
306
css_classes = ["d0-card-body"]
-
306
css_classes += ["d0-card-content", card.safe_set_keys] if @content_body
-
306
classy(*css_classes)
-
end
-
-
1
def wrap_main
-
224
return yield if no_main_wrap?
-
-
224
wrap_with :div, yield, id: "main"
-
end
-
-
1
def no_main_wrap?
-
224
Env.ajax? || params[:layout] == "none"
-
end
-
-
1
def wrap_with tag, content_or_args={}, html_args={}
-
16692
content = block_given? ? yield : content_or_args
-
16692
tag_args = block_given? ? content_or_args : html_args
-
33384
content_tag(tag, tag_args) { output(content).to_s.html_safe }
-
end
-
-
1
def wrap_each_with tag, content_or_args={}, args={}
-
content = block_given? ? yield(args) : content_or_args
-
args = block_given? ? content_or_args : args
-
content.compact.map do |item|
-
wrap_with(tag, args) { item }
-
end.join "\n"
-
end
-
-
1
private
-
-
1
def html_escape_except_quotes string
-
# to be used inside single quotes (makes for readable json attributes)
-
1200
string.to_s.gsub(/&/, "&")
-
.gsub(/\'/, "'")
-
.gsub(/>/, ">")
-
.gsub(/</, "<")
-
end
-
-
1
wrapper :div, :div
-
1
wrapper :em, :em
-
-
1
wrapper :none do
-
interior
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/wrapper.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Discussion" cards
-
#
-
1
module Discussion;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/right/discussion.rb"; end
-
1
view :titled, unknown: true do
-
voo.show :comment_box
-
super()
-
end
-
-
1
view :open, unknown: true do
-
voo.show :comment_box
-
super()
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/right/discussion.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Head" cards
-
#
-
1
module Head;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/right/head.rb"; end
-
1
def ok_to_read
-
224
true
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :head_content do
-
224
process_content render_raw
-
end
-
-
1
view :one_line_content do
-
raw_one_line_content
-
end
-
-
1
view :core do
-
process_content ::CodeRay.scan(render_raw, :html).div
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/right/head.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+TypePlusRight" cards
-
#
-
1
module TypePlusRight;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/right/type_plus_right.rb"; end
-
1
def related_sets _with_self=false
-
[[name, Card::Set::TypePlusRight.label(name.left)],
-
["#{name[1]}+*right", Card::Set::Right.label(name[1])]]
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/right/type_plus_right.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+WhenCreated" cards
-
#
-
1
module WhenCreated;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/right/when_created.rb"; end
-
1
def content
-
return "" unless left&.real?
-
I18n.localize left.created_at, format: :card_dayofwk_min_tz
-
end
-
-
# view :core, :raw
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/right/when_created.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+WhenLastEdited" cards
-
#
-
1
module WhenLastEdited;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/right/when_last_edited.rb"; end
-
1
def content
-
return "" unless left&.real?
-
I18n.localize left.updated_at, format: :card_dayofwk_min_tz
-
end
-
-
# view :core, :raw
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/right/when_last_edited.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Alerts"
-
#
-
1
module Alerts;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/self/alerts.rb"; end
-
1
def content
-
"<!-- *alerts is deprecated. please remove from layout -->"
-
end
-
-
# view :core, :raw
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/self/alerts.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Cardtype"
-
#
-
1
module Cardtype;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/self/cardtype.rb"; end
-
GROUP = {
-
1
"Text" => %w[RichText PlainText Markdown Phrase HTML],
-
"Data" => %w[Number Toggle Date URI],
-
"Upload" => %w[File Image],
-
"Custom" => [],
-
"Organize" => ["List", "Pointer", "Search", "Link list", "Nest list",
-
"Mirror List", "Mirrored List"],
-
"Template" => ["Notification template", "Email template", "Twitter template"],
-
"Admin" => ["Cardtype", "User", "Role", "Sign up", "Session", "Set", "Setting"],
-
"Styling" => ["Layout", "Skin", "Bootswatch skin", "Customized bootswatch skin", "CSS", "SCSS"],
-
"Scripting" => %w[JSON JavaScript CoffeeScript]
-
}.freeze
-
-
#DEFAULT_RULE_GROUPS = ["Text", "Data", "Upload", "Organize - Search"]
-
#STRUCTURE_RULE_GROUPS = ["Text", "Organize > Search"]
-
-
# group for each cardtype: { "RichText => "Content", "Layout" => "Admin", ... }
-
1
GROUP_MAP = GROUP.each_with_object({}) do |(cat, types), h|
-
46
types.each { |t| h[t] = cat }
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :grouped_list do
-
GROUP.keys.map do |group|
-
type_list = group == "Custom" ? custom_types : GROUP[group]
-
next if type_list.empty?
-
-
[wrap_with(:h5, group), wrap_with(:p, listing(type_list))]
-
end.flatten.join "\n"
-
end
-
-
1
def custom_types
-
custom_types = []
-
-
Card.search(type_id: Card::CardtypeID, return: "name").each do |name|
-
next if ::Card::Set::Self::Cardtype::GROUP_MAP[name]
-
-
custom_types << name
-
end
-
custom_types
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/self/cardtype.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Codenames"
-
#
-
1
module Codenames;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/self/codenames.rb"; end
-
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/self/codenames.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Foot"
-
#
-
1
module Foot;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/self/foot.rb"; end
-
-
1
def content
-
"<!-- *foot is deprecated. please remove from layout -->"
-
end
-
-
# view :core, :raw
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/self/foot.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Home"
-
#
-
1
module Home;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/self/home.rb"; end
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
view :home_url, perms: :none do
-
card_url ""
-
end
-
-
1
view :home_path, perms: :none do
-
448
card_path ""
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/self/home.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Now"
-
#
-
1
module Now;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/self/now.rb"; end
-
-
1
def content
-
I18n.localize(Time.now, format: :card_dayofwk_min_tz)
-
end
-
-
# view :core, :raw
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/self/now.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Sidebar"
-
#
-
1
module Sidebar;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/self/sidebar.rb"; end
-
1
def raw_help_text
-
<<-TEXT
-
Sidebar content of [[Layouts]] with sidebar. [[http://decko.org/sidebar|more]]
-
TEXT
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/self/sidebar.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Basic" cards
-
#
-
1
module Basic;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/type/basic.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :open_content do
-
with_table_of_contents _render_core
-
end
-
-
1
view :titled_content do
-
175
with_table_of_contents _render_core
-
end
-
-
1
def with_table_of_contents content
-
175
table_of_contents(content) || content
-
end
-
-
1
def table_of_contents content
-
175
return if nest_mode == :compact || !content.present?
-
-
174
min = card.rule(:table_of_contents).to_i
-
174
return unless min && min > 0
-
-
toc = toc_items content
-
if toc.flatten.length >= min
-
content.replace(
-
%( <div class="table-of-contents"> <h5>#{tr(:toc)}</h5> ) +
-
make_table_of_contents_list(toc) + "</div>" + content
-
)
-
end
-
end
-
-
1
def toc_items content
-
toc = []
-
dep = 1
-
content.gsub!(/<(h\d)>(.*?)<\/h\d>/i) do |match|
-
if $LAST_MATCH_INFO
-
tag, value = $LAST_MATCH_INFO[1, 2]
-
value = strip_tags(value).strip
-
next if value.empty?
-
item = { value: value, uri: URI.escape(value) }
-
case tag.downcase
-
when "h1"
-
item[:depth] = dep = 1
-
toc << item
-
when "h2"
-
toc << [] if dep == 1
-
item[:depth] = dep = 2
-
toc.last << item
-
end
-
%(<a name="#{item[:uri]}"></a>#{match})
-
end
-
end
-
toc
-
end
-
-
1
def make_table_of_contents_list items
-
list = items.map do |i|
-
if i.is_a?(Array)
-
make_table_of_contents_list(i)
-
else
-
%(<li><a href="##{i[:uri]}"> #{i[:value]}</a></li>)
-
end
-
end.join("\n")
-
"<ol>" + list + "</ol>"
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/type/basic.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Cardtype" cards
-
#
-
1
module Cardtype;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/type/cardtype.rb"; end
-
1
include_set Abstract::CqlSearch
-
-
1
def cql_content
-
2
{ type_id: id, sort: :name }
-
end
-
-
1
def related_sets with_self=false
-
sets = []
-
sets << ["#{name}+*type", Card::Set::Type.label(name)] if known?
-
sets + super
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :type, unknown: true do
-
7
link_to_card card.type_card, nil, class: "cardtype"
-
end
-
-
1
def type_formgroup args={}
-
if card.cards_of_type_exist?
-
wrap_with :div, tr(:cards_exist, cardname: safe_name)
-
else
-
super
-
end
-
end
-
-
1
view :add_link do
-
add_link
-
end
-
-
1
view :add_button do
-
7
add_link class: "btn btn-secondary"
-
end
-
-
1
def add_link opts={}
-
7
voo.title ||= tr(:add_card, cardname: safe_name)
-
7
link_to render_title, add_link_opts(opts)
-
end
-
-
1
def add_link_opts opts
-
7
modal = opts.delete :modal
-
7
modal = true if modal.nil?
-
7
opts[:path] = add_path(modal ? :new_in_modal : :new)
-
7
modal ? modal_link_opts(opts) : opts
-
end
-
-
1
view :add_url do
-
card_url _render_add_path
-
end
-
-
1
def add_path view
-
7
path_args = { mark: card.name }
-
7
process_voo_params(path_args) if voo.params
-
7
if view == :new
-
path_args[:action] = :new
-
else
-
7
path_args[:action] = :type
-
7
path_args[:view] = view
-
end
-
7
path path_args
-
end
-
-
# don't cache because it depends on update permission for another card
-
1
view :configure_link, cache: :never, perms: ->(fmt) { fmt.can_configure? } do
-
configure_link
-
end
-
-
1
def can_configure?
-
7
Card.fetch(card, :type, :structure, new: {}).ok? :update
-
end
-
-
1
view :configure_button, cache: :never, denial: :blank,
-
7
perms: ->(fmt) { fmt.can_configure? } do
-
7
configure_link "btn btn-secondary"
-
end
-
-
1
def configure_link css_class=nil
-
7
return "" unless Card.fetch(card, :type, :structure, new: {}).ok? :update
-
-
7
voo.title ||= tr(:configure_card, cardname: safe_name.pluralize)
-
7
title = _render_title
-
7
link_to_card card, title, path: { view: :bridge, bridge: { tab: :rules_tab },
-
set: Card::Name[safe_name, :type] },
-
class: css_classes("configure-type-link ml-3", css_class)
-
end
-
-
1
private
-
-
1
def process_voo_params path_args
-
context = ((@parent&.card) || card).name
-
Rack::Utils.parse_nested_query(voo.params).each do |key, value|
-
value = value.to_name.absolute(context) if value
-
key = key.to_name.absolute(context)
-
path_args[key] = value
-
end
-
end
-
end
-
-
1
include Basic
-
-
1
def cards_of_type_exist?
-
!new_card? && Card.where(trash: false, type_id: id).exists?
-
end
-
-
1
def create_ok?
-
Card.new(type_id: id).ok? :create
-
end
-
-
1
def was_cardtype?
-
7
type_id_before_act == Card::CardtypeID
-
end
-
-
1
event :check_for_cards_of_type, after: :validate_delete do
-
errors.add :cardtype, tr(:cards_exist, cardname: name) if cards_of_type_exist?
-
end
-
-
1
event :check_for_cards_of_type_when_type_changed,
-
:validate, changing: :type, when: :was_cardtype? do
-
if cards_of_type_exist?
-
errors.add :cardtype, tr(:error_cant_alter, name: name_before_act)
-
end
-
end
-
-
1
event :validate_cardtype_name, :validate, on: :save, changed: :name do
-
7
if %r{[<>/]}.match?(name)
-
errors.add :name, tr(:error_invalid_character_in_cardtype, banned: "<, >, /")
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/type/cardtype.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "LayoutType" cards
-
#
-
# -*- encoding : utf-8 -*-
-
1
module LayoutType;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/type/layout_type.rb"; end
-
-
1
include_set Type::Html
-
-
1
event :update_layout_registry, :finalize, on: :update do
-
Card::Layout.deregister_layout name
-
Card::Layout.register_layout_with_nest name, format
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
with_nest_mode :template do
-
process_content ::CodeRay.scan(_render_raw, :html).div
-
end
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/type/layout_type.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "NotificationTemplate" cards
-
#
-
1
module NotificationTemplate;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/type/notification_template.rb"; end
-
1
card_reader :contextual_class
-
1
card_reader :disappear
-
1
card_reader :message
-
-
1
def deliver context
-
success.flash alert_message(context)
-
end
-
-
1
def alert_message context
-
mcard = message.present? ? message_card : self
-
format(:html).alert_message context, mcard
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def alert_message context, message_card
-
mformat = subformat message_card
-
alert card.alert_class, true, card.disappear? do
-
mformat.contextual_content context, view: alert_view(mformat)
-
end
-
end
-
-
1
def alert_view format
-
format.respond_to?(:notify) ? format.notify : :core
-
end
-
end
-
-
1
def disappear?
-
disappear.present? ? disappear_card.checked? : true
-
end
-
-
1
def alert_class
-
contextual_class.present? ? contextual_class_card.item_name : :success
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/type/notification_template.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Number" cards
-
#
-
1
module Number;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/type/number.rb"; end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def input_type
-
:text_field
-
end
-
end
-
-
1
event :validate_number, :validate, on: :save do
-
errors.add :content, tr(:not_numeric, content: content) unless valid_number?(content)
-
end
-
-
1
def valid_number? string
-
return true if string.empty?
-
-
valid = true
-
begin
-
Kernel.Float(string)
-
rescue ArgumentError, TypeError
-
valid = false
-
end
-
valid
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/type/number.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Phrase" cards
-
#
-
1
module Phrase;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/type/phrase.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def input_type
-
99
:text_field
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/type/phrase.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Session" cards
-
#
-
1
module Session;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/type/session.rb"; end
-
1
include_set Pointer
-
-
1
def virtual?
-
164
session_content.present?
-
end
-
-
1
def history?
-
false
-
end
-
-
1
def followable?
-
false
-
end
-
-
1
def recaptcha_on?
-
380
false
-
end
-
-
1
def session_key
-
508
"_card_#{key}"
-
end
-
-
1
def session_content
-
508
Env.session[session_key]
-
end
-
-
1
def session_content= val
-
Env.session[session_key] = val
-
end
-
-
1
def content
-
970
db_content || session_content
-
end
-
-
1
event :store_in_session, :prepare_to_store, on: :save do
-
self.session_content = db_content
-
abort :success
-
end
-
-
1
event :delete_in_session, :prepare_to_store, on: :delete do
-
self.session_content = nil
-
abort :success
-
end
-
-
1
def ok_to_create
-
true
-
end
-
-
1
def ok_to_update
-
true
-
end
-
-
1
def add_to_trash args
-
yield args.merge trash: true
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
before :core do
-
voo.items[:view] = :name
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/type/session.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Toggle" cards
-
#
-
1
module Toggle;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/type/toggle.rb"; end
-
1
def checked?
-
content == "1"
-
end
-
-
1
view :core do
-
3
case card.content.to_i
-
1
when 1 then tr(:yes)
-
2
when 0 then tr(:no)
-
else
-
"?"
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :input do
-
1
toggle
-
end
-
-
1
view :labeled_editor do
-
toggle + toggle_label
-
end
-
-
1
def toggle
-
1
check_box :content
-
end
-
-
1
def toggle_label
-
label :content, card.name.tag
-
end
-
-
1
def one_line_content
-
short_content
-
end
-
-
1
def short_content
-
render_core
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/type/toggle.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Uri" cards
-
#
-
1
module Uri;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/type/uri.rb"; end
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
link_to_resource _render_raw, render_title
-
end
-
-
1
view :url_link do
-
link_to_resource _render_raw
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def input_type
-
:text_field
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/type/uri.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (EmailField)
-
#
-
1
module EmailField;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/abstract/email_field.rb"; end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
# turn off autodetection of uri's
-
1
def chunk_list
-
:references
-
end
-
end
-
-
# format :html do
-
# def pointer_items args
-
# card.item_names(context: :raw).map do |iname|
-
# wrap_item iname, args
-
# end
-
# end
-
# end#
-
-
2
module EmailTextFormat; module_parent.send :register_set_format, Card::Format::EmailTextFormat, self; extend Card::Set::AbstractFormat
-
1
def email_addresses context
-
21
context ||= self
-
21
card.item_names(context: context.name).map do |name|
-
# FIXME: context is processed twice here because pointers absolutize
-
# item_names by default while other types can return relative names.
-
# That's poor default behavior and should be fixed!
-
21
name = name.to_name.absolute context
-
21
email_address?(name) ? name : email_address_from_card(name, context)
-
end.flatten.compact.join(", ")
-
end
-
-
1
def email_address? string
-
21
string =~ /.+\@.+\..+/
-
end
-
-
1
def email_address_from_card name, context
-
20
card = Card.fetch name
-
20
card.account&.email || email_addresses_from_card_content(card, context)
-
end
-
-
1
def email_addresses_from_card_content card, context
-
subformat(card).contextual_content(context).split(/[,\n]/)
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/abstract/email_field.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (TestContext)
-
#
-
1
module TestContext;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/abstract/test_context.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
20
return super() if voo.hide? :test_context
-
card.with_context test_context_card do
-
super()
-
end
-
end
-
-
1
def test_context_card
-
card.left.fetch(:test_context)&.item_card
-
end
-
end
-
-
2
module EmailHtmlFormat; module_parent.send :register_set_format, Card::Format::EmailHtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
20
voo.hide! :test_context
-
20
super()
-
end
-
end
-
-
2
module EmailTextFormat; module_parent.send :register_set_format, Card::Format::EmailTextFormat, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
40
voo.hide! :test_context
-
40
super()
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/abstract/test_context.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (EmailHtml)
-
#
-
1
module EmailHtml;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/all/email_html.rb"; end
-
2
module EmailHtmlFormat; module_parent.send :register_set_format, Card::Format::EmailHtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :unknown do
-
""
-
end
-
-
1
view :compact_missing do
-
""
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/all/email_html.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (EmailText)
-
#
-
1
module EmailText;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/all/email_text.rb"; end
-
-
2
module EmailTextFormat; module_parent.send :register_set_format, Card::Format::EmailTextFormat, self; extend Card::Set::AbstractFormat
-
1
view :unknown do
-
""
-
end
-
-
1
view :compact_missing do
-
""
-
end
-
-
1
view :last_action, perms: :none, cache: :never do
-
_render_last_action_verb
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/all/email_text.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Bcc" cards
-
#
-
1
module Bcc;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/right/bcc.rb"; end
-
1
include_set Abstract::EmailField
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/right/bcc.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Cc" cards
-
#
-
1
module Cc;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/right/cc.rb"; end
-
1
include_set Abstract::EmailField
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/right/cc.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+From" cards
-
#
-
1
module From;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/right/from.rb"; end
-
1
include_set Abstract::EmailField
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/right/from.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+HtmlMessage" cards
-
#
-
1
module HtmlMessage;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/right/html_message.rb"; end
-
1
include_set Abstract::TestContext
-
-
1
def clean_html?
-
false
-
end
-
-
2
module EmailHtmlFormat; module_parent.send :register_set_format, Card::Format::EmailHtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def email_content context
-
20
content = contextual_content context
-
20
return unless content.present?
-
20
Card::Mailer.layout content
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/right/html_message.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Subject" cards
-
#
-
1
module Subject;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/right/subject.rb"; end
-
1
include_set Abstract::TestContext
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/right/subject.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+TextMessage" cards
-
#
-
1
module TextMessage;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/right/text_message.rb"; end
-
1
include_set Abstract::TestContext
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/right/text_message.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+To" cards
-
#
-
1
module To;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/right/to.rb"; end
-
1
include_set Abstract::EmailField
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/right/to.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "EmailTemplate" cards
-
#
-
1
module EmailTemplate;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/type/email_template.rb"; end
-
-
1
def clean_html?
-
false
-
end
-
-
1
def deliver context=nil, fields={}, opts={}
-
21
mail = format.mail context, fields, opts
-
21
mail.deliver
-
rescue Net::SMTPError => exception
-
errors.add :exception, exception.message
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
def mail context=nil, fields={}, opts={}
-
21
config = card.email_config context, fields, opts
-
21
fmt = self # self is <Mail::Message> within the new_mail block
-
21
Card::Mailer.new_mail config do
-
21
fmt.message_body self, config
-
21
fmt.add_attachments self, config.delete(:attach)
-
end
-
end
-
-
1
def message_body mail, config
-
21
config[:html_message] &&= config[:html_message].call mail
-
21
method, args = body_method_and_args config[:html_message].present?,
-
config[:text_message].present?
-
82
args = Array.wrap(args).map { |arg| config[arg] }
-
21
send method, mail, *args
-
end
-
-
1
def body_method_and_args html, text
-
21
if html && text
-
20
[:text_and_html_message, %i[text_message html_message attach]]
-
1
elsif html
-
%i[html_body html_message]
-
else
-
1
%i[text_body text_message]
-
end
-
end
-
-
1
def text_and_html_message mail, text_message, html_message, attachment_list=nil
-
20
fmt = self
-
20
if attachment_list&.any?
-
mail.multipart_mixed text_message, html_message
-
else
-
40
mail.text_part { body text_message }
-
40
mail.html_part { fmt.html_body self, html_message }
-
end
-
end
-
-
1
def multipart_mixed mail, text_message, html_message
-
mail.content_type "multipart/mixed"
-
mail.part content_type: "multipart/alternative" do |copy|
-
copy.part content_type: "text/plain" do |plain|
-
plain.body = text_message
-
end
-
copy.part content_type: "text/html" do |html|
-
html.body = html_message
-
end
-
end
-
end
-
-
1
def html_body mail, message
-
20
mail.content_type "text/html; charset=UTF-8"
-
20
mail.body message
-
end
-
-
1
def text_body mail, message
-
1
mail.content_type "text/plain; charset=UTF-8"
-
2
mail.text_part { body message }
-
end
-
-
1
def add_attachments mail, list
-
21
return unless list.present?
-
each_valid_attachment list do |file, index|
-
mail.add_file filename: attachment_name(file, index),
-
content: File.read(file.path)
-
end
-
end
-
-
1
def each_valid_attachment list
-
list.each_with_index do |cardname, index|
-
next unless (file = Card[cardname]&.try(:attachment))
-
yield file, index
-
end
-
end
-
-
1
def attachment_name file, number
-
"attachment-#{number + 1}.#{file.extension}"
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/type/email_template.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Type; module EmailTemplate;
-
# Set: All "EmailTemplate+EmailConfig" cards (EmailConfig)
-
#
-
1
module EmailConfig;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/type/email_template/email_config.rb"; end
-
EMAIL_FIELDS =
-
1
%i[to from cc bcc attach subject text_message html_message].freeze
-
-
EMAIL_FIELD_METHODS =
-
1
{ subject: :contextual_content,
-
text_message: :contextual_content,
-
attach: :extended_item_contents }.freeze
-
-
# @param [Card] context the card in whose context all email fields will be interpreted
-
# @param [Hash] fields override any templated field configurations with hash values
-
# @param [Hash] opts options for rendering. unknown options become format options
-
# @option opts [Card, String, Integer] :auth user identifier. render as this user
-
1
def email_config context, fields={}, opts={}
-
21
@active_email_context = context || self
-
21
auth = opts.delete :auth
-
21
config = EMAIL_FIELDS.each_with_object({}) do |field, conf|
-
168
conf[field] = fields[field] || email_field_from_card(field, auth, opts)
-
end
-
21
safe_from_and_reply_to! config
-
210
config.select { |_k, v| v.present? }
-
end
-
-
1
def email_field_from_card field, auth, format_opts
-
148
return unless (field_card = fetch(field))
-
81
auth ||= field_card.updater
-
81
special_email_field_method(field, field_card, auth, format_opts) ||
-
standard_email_field(field, field_card, auth, format_opts)
-
end
-
-
1
def special_email_field_method field, field_card, auth, format_opts
-
81
method = "email_#{field}_field"
-
81
return unless respond_to? method
-
20
send method, field_card, auth, format_opts
-
end
-
-
1
def standard_email_field field, field_card, auth, format_opts
-
61
method = EMAIL_FIELD_METHODS[field] || :email_addresses
-
61
format_opts = format_opts.merge format: :email_text
-
61
Auth.as auth do
-
61
field_card.format(format_opts).send method, @active_email_context
-
end
-
end
-
-
# html messages return procs because image attachments can't be properly rendered
-
# without a mail object. (which isn't available at initial config time)
-
1
def email_html_message_field message_card, auth, format_opts
-
20
proc do |mail|
-
20
Auth.as auth do
-
20
format_opts = format_opts.merge format: :email_html, active_mail: mail
-
20
message_card.format(format_opts).email_content @active_email_context
-
end
-
end
-
end
-
-
# whenever a default "from" field is configured in Card::Mailer, emails are always
-
# actually "from" that address
-
1
def safe_from_and_reply_to! config
-
21
conf_name, conf_email = configured_from_name_and_email config[:from]
-
21
actual_email = Card::Mailer.default[:from] || conf_email
-
21
config[:from] = email_from_field_value conf_name, conf_email, actual_email
-
21
config[:reply_to] ||= actual_email
-
end
-
-
1
def email_from_field_value conf_name, conf_email, actual_email
-
21
conf_text = conf_name || conf_email
-
21
if conf_text != actual_email
-
%("#{conf_text}" <#{actual_email}>)
-
21
elsif actual_email.present?
-
19
actual_email
-
else
-
2
Card[WagnBotID].account.email
-
end
-
end
-
-
1
def configured_from_name_and_email raw_string
-
21
if raw_string =~ /(.*)\<(.*)>/
-
[Regexp.last_match(1).strip, Regexp.last_match(2)]
-
else
-
21
[nil, raw_string]
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/type/email_template/email_config.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (AccountField)
-
#
-
1
module AccountField;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/abstract/account_field.rb"; end
-
-
# allow account owner to update account field content
-
1
def ok_to_update
-
6
return true if own_account? && !name_changed? && !type_id_changed?
-
-
2
super
-
end
-
-
# force inherit permission on create
-
# (cannot be done with rule, because sets are not addressable)
-
1
def permission_rule_id action
-
12
if action == :create
-
4
left_permission_rule_id action
-
else
-
8
super
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/abstract/account_field.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (Accountable)
-
#
-
1
module Accountable;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/abstract/accountable.rb"; end
-
1
def account
-
117
fetch :account, new: {}
-
end
-
-
1
def default_account_status
-
1
"active"
-
end
-
-
1
def current_account?
-
id && Auth.current_id == id
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def default_bridge_tab
-
card.current_account? ? :account_tab : super
-
end
-
-
1
view :account_tab do
-
bridge_pill_sections "Account" do
-
[["Settings", account_details_items],
-
["Content", account_content_items]]
-
end
-
end
-
-
1
def show_account_tab?
-
card.account.real?
-
end
-
-
1
def account_formgroups
-
2
Auth.as_bot do
-
2
subformat(card.account)._render :content_formgroups, structure: true
-
end
-
end
-
-
1
def account_details_items
-
[
-
["Email and Password", :account,
-
{ path: { slot: { hide: %i[help_link bridge_link] } } }],
-
["Roles", :roles,
-
{ path: { view: :content_with_edit_button } }],
-
["Notifications", :follow]
-
]
-
end
-
-
1
def account_content_items
-
[["Created", :created],
-
["Edited", :edited]]
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/abstract/accountable.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Account)
-
#
-
1
module Account;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/all/account.rb"; end
-
1
module ClassMethods
-
1
def default_accounted_type_id
-
5
UserID
-
end
-
end
-
-
1
def account
-
34
fetch :account
-
end
-
-
1
def parties
-
318
@parties ||= (all_enabled_roles << id).flatten.reject(&:blank?)
-
end
-
-
1
def among? ok_ids
-
298
ok_ids.any? do |ok_id|
-
298
ok_id == AnyoneID ||
-
148
(ok_id == AnyoneWithRoleID && all_enabled_roles.size > 1) ||
-
parties.member?(ok_id)
-
end
-
end
-
-
1
def own_account?
-
# card is +*account card of signed_in user.
-
18
name.part_names[0].key == Auth.as_card.key &&
-
name.part_names[1].key == Card[:account].key
-
end
-
-
1
def read_rules
-
196
@read_rules ||= fetch_read_rules
-
end
-
-
1
def read_rules_hash
-
3363
@read_rules_hash ||= read_rules.each_with_object({}) { |id, h| h[id] = true }
-
end
-
-
1
def fetch_read_rules
-
170
return [] if id == WagnBotID # always_ok, so not needed
-
-
170
([AnyoneID] + parties).each_with_object([]) do |party_id, rule_ids|
-
573
next unless (cache = Card::Rule.read_rule_cache[party_id])
-
170
rule_ids.concat cache
-
end
-
end
-
-
1
def clear_roles
-
1
@parties = @all_roles = @all_active_roles = @read_rules = nil
-
end
-
-
1
def with_clear_roles
-
a, b, c, d = @parties, @all_roles, @all_active_roles, @read_rules
-
yield
-
ensure
-
@parties, @all_roles, @all_active_roles, @read_rules = a, b, c, d
-
end
-
-
1
def all_enabled_roles
-
261
@all_active_roles ||= (id == AnonymousID ? [] : enabled_role_ids)
-
end
-
-
1
def all_roles
-
@all_roles ||= (id == AnonymousID ? [] : fetch_roles)
-
end
-
-
1
def enabled_role_ids
-
164
Auth.as_bot do
-
# workaround for broken migrations
-
164
return fetch_roles unless Card::Codename.exists? :enabled_roles
-
-
164
enabled = enabled_roles_card
-
164
enabled.virtual? ? enabled.item_ids : fetch_roles
-
end
-
end
-
-
1
def enabled_roles_card
-
164
fetch :enabled_roles, eager_cache: true, new: { type_id: SessionID }
-
end
-
-
1
def fetch_roles
-
164
[AnyoneSignedInID] + role_ids_from_roles_trait
-
end
-
-
1
def role_ids_from_roles_trait
-
164
Auth.as_bot do
-
164
role_trait = fetch :roles
-
164
role_trait ? role_trait.item_ids : []
-
end
-
end
-
-
1
event :generate_token do
-
Digest::SHA1.hexdigest "--#{Time.zone.now.to_f}--#{rand 10}--"
-
end
-
-
1
event :set_stamper, :prepare_to_validate do
-
451
self.updater_id = Auth.current_id
-
451
self.creator_id = updater_id if new_card?
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/all/account.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Account" cards
-
#
-
# -*- encoding : utf-8 -*-
-
1
module Account;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/right/account.rb"; end
-
-
1
card_accessor :email
-
1
card_accessor :password
-
1
card_accessor :salt
-
1
card_accessor :status
-
1
card_accessor :api_key
-
-
1
require_field :email
-
-
1
def accounted
-
3
left
-
end
-
-
1
def accounted_id
-
8
left_id
-
end
-
-
1
def ok_to_read
-
5
own_account? ? true : super
-
end
-
-
# allow account owner to update account field content
-
1
def ok_to_update
-
3
return true if own_account? && !name_changed? && !type_id_changed?
-
-
1
super
-
end
-
-
1
def changes_visible? act
-
17
act.actions_affecting(act.card).each do |action|
-
17
return true if action.card.ok? :read
-
end
-
false
-
end
-
-
1
def send_account_email email_template
-
3
ecard = Card[email_template]
-
3
unless ecard&.type_id == EmailTemplateID
-
raise Card::Error, "invalid email template: #{email_template}"
-
end
-
-
3
ecard.deliver self, to: email
-
end
-
-
1
def validate_api_key! api_key
-
api_key_card.validate! api_key
-
end
-
-
1
def method_missing method, *args
-
72
super unless args.empty? && (matches = method.match(/^(?<status>.*)\?$/))
-
-
72
status == matches[:status]
-
end
-
-
1
def respond_to_missing? method, _include_private=false
-
8
method.match?(/\?/) ? true : super
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/right/account.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Right; module Account;
-
# Set: All "+Account+Events" cards (Events)
-
#
-
#### ON CREATE
-
1
module Events;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/right/account/events.rb"; end
-
-
1
event :set_default_salt, :prepare_to_validate, on: :create do
-
2
add_subfield(:salt).generate
-
end
-
-
1
event :set_default_status, :prepare_to_validate, on: :create do
-
2
add_subfield :status, content: (accounted&.try(:default_account_status) || "active")
-
end
-
-
# ON UPDATE
-
-
# reset password emails contain a link to update the +*account card
-
# and trigger this event
-
1
event :reset_password, :prepare_to_validate, on: :update, trigger: :required do
-
1
verifying_token :reset_password_success, :reset_password_failure
-
end
-
-
1
event :verify_and_activate, :prepare_to_validate, on: :update, trigger: :required do
-
1
activatable do
-
1
verifying_token :verify_and_activate_success, :verify_and_activate_failure
-
1
add_subcard(accounted)&.try :activate_accounted
-
end
-
end
-
-
1
event :password_redirect, :finalize, on: :update, when: :password_redirect? do
-
success << { id: name, view: "edit" }
-
end
-
-
# INTEGRATION
-
-
1
%i[password_reset_email verification_email welcome_email].each do |email|
-
3
event "send_#{email}".to_sym, :integrate, trigger: :required do
-
3
send_account_email email
-
end
-
end
-
-
## EVENT HELPERS
-
-
1
def activatable
-
1
abort :failure, "no field manipulation mid-activation" if subcards.present?
-
# above is necessary because activation uses super user (Decko Bot),
-
# so allowing subcards would be unsafe
-
1
yield
-
end
-
-
# note: this only works in the context of an action.
-
# if run independently, it will not activate an account
-
1
event :activate_account do
-
1
add_subfield :status, content: "active"
-
1
trigger_event! :send_welcome_email
-
end
-
-
1
def verifying_token success, failure
-
2
requiring_token do |token|
-
2
result = Auth::Token.decode token
-
2
if result.is_a?(String)
-
aborting { send failure, result }
-
else
-
2
send success
-
end
-
end
-
end
-
-
1
def requiring_token
-
2
if !(token = Env.params[:token])
-
aborting { errors.add :token, "is required" }
-
else
-
2
yield token
-
end
-
end
-
-
1
def password_redirect?
-
2
Auth.current_id == accounted_id && password.blank?
-
end
-
-
1
def verify_and_activate_success
-
1
Auth.signin accounted_id
-
1
Auth.as_bot # use admin permissions for rest of action
-
1
activate_account
-
1
success << ""
-
end
-
-
1
def verify_and_activate_failure error_message
-
send_verification_email
-
errors.add "Sorry, #{error_message}. Please check your email for a new activation link."
-
end
-
-
1
def reset_password_success
-
1
Auth.signin accounted_id
-
1
success << { id: name, view: :edit }
-
1
abort :success
-
end
-
-
1
def reset_password_failure error_message
-
Auth.as_bot { send_password_reset_email }
-
errors.add tr(:sorry_email_reset, error_msg: error_message)
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/right/account/events.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Right; module Account;
-
# Set: All "+Account+Views" cards (Views)
-
#
-
1
module Views;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/right/account/views.rb"; end
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
view :verify_url, cache: :never do
-
2
raise Error::PermissionDenied unless card.ok?(:create) || card.action
-
-
2
token_url :verify_and_activate, anonymous: true
-
end
-
-
1
view :reset_password_url do
-
2
raise Error::PermissionDenied unless card.password_card.ok? :update
-
-
2
token_url :reset_password
-
end
-
-
1
view :token_expiry do
-
"(#{token_expiry_sentence}"
-
end
-
-
1
view :token_days do
-
4
Card.config.token_expiry / 1.day
-
end
-
-
# DEPRECATED
-
1
view :verify_days, :token_days
-
1
view :reset_password_days, :token_days
-
-
1
def token_url trigger, extra_payload={}
-
4
card_url path(action: :update,
-
card: { trigger: trigger },
-
token: new_token(extra_payload))
-
end
-
-
1
def token_expiry_sentence
-
"Link will expire in #{render_token_days} days"
-
end
-
-
1
def new_token extra_payload
-
4
Auth::Token.encode card.accounted_id, extra_payload
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
3
[account_field_nest(:email, "email"),
-
account_field_nest(:password, "password")]
-
end
-
-
1
def account_field_nest field, title
-
6
field_nest field, title: title, view: :labeled
-
# edit: :inline, hide: [:help_link, :bridge_link]
-
end
-
-
1
before :content_formgroups do
-
3
voo.edit_structure = [[:email, "email"], [:password, "password"]]
-
end
-
-
1
view :token_expiry do
-
"<p><em>#{token_expiry_sentence}</em></p>"
-
end
-
end
-
-
2
module EmailHtmlFormat; module_parent.send :register_set_format, Card::Format::EmailHtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def mail context, fields
-
super context, fields.reverse_merge(to: card.email)
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/right/account/views.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+ApiKey" cards
-
#
-
1
module ApiKey;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/right/api_key.rb"; end
-
1
include_set Abstract::AccountField
-
-
# DURATIONS = "second|minute|hour|day|week|month|year".freeze
-
-
1
def history?
-
false
-
end
-
-
1
view :raw do
-
tr :private_data
-
end
-
-
1
def validate! api_key
-
error =
-
case
-
when !real? then [:token_not_found, tr(:error_token_not_found)]
-
# when expired? then [:token_expired, tr(:error_token_expired)]
-
when content != api_key then [:incorrect_token, tr(:error_incorrect_token)]
-
end
-
errors.add(*error) if error
-
error.nil?
-
end
-
-
# def expired?
-
# !permanent? && updated_at <= term.ago
-
# end
-
#
-
# def permanent?
-
# term == "permanent"
-
# end
-
-
# def term
-
# @term ||=
-
# if expiration.present?
-
# term_from_string expiration
-
# else
-
# Card.config.token_expiry
-
# end
-
# end
-
-
# def term_from_string string
-
# string.strip!
-
# return "permanent" if string == "none"
-
# re_match = /^(\d+)[\.\s]*(#{DURATIONS})s?$/.match(string)
-
# number, unit = re_match.captures if re_match
-
# raise Card::Open::Error, tr(:exception_bad_expiration, example: '2 days') unless unit
-
# number.to_i.send unit
-
# end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/right/api_key.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Email" cards
-
#
-
# -*- encoding : utf-8 -*-
-
1
module Email;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/right/email.rb"; end
-
-
1
include_set Abstract::AccountField
-
-
1
event :validate_email, :validate, on: :save do
-
3
if content? && content !~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
-
errors.add :content, tr(:error_invalid_address)
-
end
-
end
-
-
1
event :validate_unique_email, after: :validate_email, on: :save do
-
3
if content.present?
-
3
Auth.as_bot do
-
3
cql = { right_id: EmailID, eq: content, return: :id }
-
3
cql[:not] = { id: id } if id
-
3
cql_comment = tr(:search_email_duplicate, content: content)
-
3
if Card.search(cql, cql_comment).first
-
errors.add :content, tr(:error_not_unique)
-
end
-
end
-
end
-
end
-
-
1
event :downcase_email, :prepare_to_validate, on: :save do
-
75
return if !content || content == content.downcase
-
self.content = content.downcase
-
end
-
-
1
def email_required?
-
!left.system?
-
end
-
-
1
def ok_to_read
-
4
if own_email? || Auth.always_ok?
-
4
true
-
else
-
deny_because tr(:deny_email_restricted)
-
end
-
end
-
-
1
def own_email?
-
4
name.part_names[0].key == Auth.as_card.key
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/right/email.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Password" cards
-
#
-
1
module Password;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/right/password.rb"; end
-
1
include_set Abstract::AccountField
-
-
1
def history?
-
78
false
-
end
-
-
1
def ok_to_read
-
3
own_account? ? true : super
-
end
-
-
1
event :encrypt_password, :store,
-
on: :save, changed: :content,
-
3
when: proc { !Card::Env[:no_password_encryptions] } do
-
# no_password_encryptions = hack for import - fix with api for ignoring events
-
3
salt = left&.salt
-
3
self.content = Auth.encrypt content, salt
-
-
# errors.add :password, 'need a valid salt'
-
# turns out we have a lot of existing account without a salt.
-
# not sure when that broke??
-
end
-
-
1
event :validate_password, :validate, on: :save do
-
3
return if content.length > 3
-
-
errors.add :password, tr(:password_length)
-
end
-
-
1
event :validate_password_present, :prepare_to_validate, on: :update do
-
1
abort :success if content.blank?
-
end
-
-
1
view :raw do
-
3
tr :encrypted
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :core, wrap: :em do
-
3
render_raw
-
end
-
-
1
view :input do
-
81
card.content = ""
-
81
password_field :content, class: "d0-card-content", autocomplete: autocomplete?
-
end
-
-
1
def autocomplete?
-
81
return "on" if @parent && @parent.card.name == "*signin+*account" # HACK
-
81
"off"
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/right/password.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Roles" cards
-
#
-
1
module Roles;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/right/roles.rb"; end
-
1
event :validate_permission_to_assign_roles, :validate, on: :save do
-
1
return unless (fr = forbidden_roles).present?
-
-
errors.add :permission_denied,
-
"You don't have permission to assign the role#{'s' if fr.size > 1} "\
-
"#{fr.map(&:name).to_sentence}" # LOCALIZE
-
end
-
-
1
def forbidden_roles
-
# restore old roles for permission check
-
1
with_old_role_permissions do |new_roles|
-
1
new_roles.select do |card|
-
3
!Card.fetch(card, "*members").ok? :update
-
end
-
end
-
end
-
-
1
def with_old_role_permissions
-
1
new_roles = item_cards
-
1
new_content = content
-
1
left.clear_roles
-
1
Auth.update_always_cache Card::Auth.as_id, nil
-
1
self.content = db_content_before_act
-
1
yield new_roles
-
ensure
-
1
self.content = new_content
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/right/roles.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Salt" cards
-
#
-
1
module Salt;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/right/salt.rb"; end
-
1
include_set Abstract::AccountField
-
-
1
def generate
-
2
self.content = Digest::SHA1.hexdigest "--#{Time.zone.now}--"
-
end
-
-
1
def history?
-
4
false
-
end
-
-
1
view :raw do
-
tr :private_data
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/right/salt.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Status" cards
-
#
-
1
module Status;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/right/status.rb"; end
-
1
include_set Abstract::AccountField
-
1
include_set Abstract::Pointer
-
-
1
def input_type
-
:radio
-
end
-
-
1
def option_names
-
%w[unapproved unverified active blocked system]
-
end
-
-
1
def ok_to_update
-
1
if own_account? && !Auth.always_ok?
-
deny_because you_cant(tr(:deny_not_change_own_account))
-
else
-
1
super
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/right/status.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Signin"
-
#
-
# The Sign In card manages logging in and out of the site.
-
#
-
1
module Signin;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/self/signin.rb"; end
-
# /:signin (core view) gives the login ui
-
# /:signin?view=edit gives the forgot password ui
-
-
# /update/:signin is the login action
-
# /delete/:signin is the logout action
-
-
# authentication event
-
1
event :signin, :validate, on: :update do
-
71
email = subfield :email
-
71
email &&= email.content
-
71
pword = subfield :password
-
71
pword &&= pword.content
-
-
71
authenticate_or_abort email, pword
-
end
-
-
# abort after successful signin (do not save card)
-
1
event :signin_success, after: :signin do
-
71
abort :success
-
end
-
-
1
event :signout, :validate, on: :delete do
-
6
Env.reset_session
-
6
Auth.signin AnonymousID
-
6
abort :success
-
end
-
-
# triggered by clicking "Reset my Password", this sends out the verification password
-
# and aborts (does not sign in)
-
1
event :send_reset_password_token, before: :signin, on: :update, trigger: :required do
-
1
email = subfield(:email)&.content
-
1
send_reset_password_email_or_fail email
-
end
-
-
1
def ok_to_read
-
79
true
-
end
-
-
1
def recaptcha_on?
-
158
false
-
end
-
-
1
def i18n_signin key
-
1
I18n.t key, scope: "mod.card-mod-account.set.self.signin"
-
end
-
-
1
def authenticate_or_abort email, pword
-
71
abort_unless email, :email_missing
-
71
abort_unless pword, :password_missing
-
71
authenticate_and_signin(email, pword) || failed_signin(email)
-
end
-
-
1
def authenticate_and_signin email, pword
-
71
return unless (account = Auth.authenticate email, pword)
-
-
71
Auth.signin account.left_id
-
end
-
-
1
def failed_signin email
-
errors.add :signin, signin_error_message(account_for(email))
-
abort :failure
-
end
-
-
1
def abort_unless value, error_key
-
142
abort :failure, i18n_signin(error_key) unless value
-
end
-
-
1
def signin_error_message account
-
case
-
when account.nil? then i18n_signin(:error_unknown_email)
-
when !account.active? then i18n_signin(:error_not_active)
-
else i18n_signin(:error_wrong_password)
-
end
-
end
-
-
1
def error_on field, error_key
-
errors.add field, i18n_signin(error_key)
-
end
-
-
1
def account_for email
-
1
Auth.find_account_by_email email
-
end
-
-
1
def send_reset_password_email_or_fail email
-
aborting do
-
break errors.add :email, i18n_signin(:error_blank) if email.blank?
-
-
if (account = Auth.find_account_by_email(email))&.active?
-
Auth.as_bot { account.send_password_reset_email }
-
elsif account
-
errors.add :account, i18n_signin(:error_not_active)
-
else
-
errors.add :email, i18n_signin(:error_not_recognized)
-
end
-
end
-
end
-
-
1
def send_reset_password_email_or_fail email
-
1
aborting do
-
1
break if blank_email? email
-
-
1
if (account = account_for email)&.active?
-
1
send_reset_password_email account
-
else
-
reset_password_fail account
-
end
-
end
-
end
-
-
1
def blank_email? email
-
1
return false if email.present?
-
-
error_on :email, :error_blank
-
end
-
-
1
def send_reset_password_email account
-
2
Auth.as_bot { account.send_password_reset_email }
-
end
-
-
1
def reset_password_fail account
-
if account
-
error_on :account, :error_not_active
-
else
-
error_on :email, :error_not_recognized
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :core, cache: :never do
-
78
voo.edit_structure = [signin_field(:email), signin_field(:password)]
-
78
with_nest_mode :edit do
-
78
card_form :update, recaptcha: :off, success: signin_success do
-
[
-
78
_render_content_formgroups,
-
_render_signin_buttons
-
]
-
end
-
end
-
end
-
-
1
view :open do
-
voo.show :help
-
voo.hide :menu
-
super()
-
end
-
-
# FIXME: need a generic solution for this
-
1
view :title do
-
80
voo.title ||= I18n.t(:sign_in_title, scope: "mod.card-mod-account.set.self.signin")
-
80
super()
-
end
-
-
1
view :open_content do
-
# annoying step designed to avoid table of contents. sigh
-
_render_core
-
end
-
-
1
view :one_line_content do
-
""
-
end
-
-
1
view :reset_password_success do
-
# 'Check your email for a link to reset your password'
-
2
frame { I18n.t(:check_email, scope: "mod.card-mod-account.set.self.signin") }
-
end
-
-
1
view :signin_buttons do
-
78
button_formgroup do
-
78
[signin_button, signup_link, reset_password_link]
-
end
-
end
-
-
# FORGOT PASSWORD
-
1
view :edit do
-
1
reset_password_voo
-
2
Auth.as_bot { super() }
-
end
-
-
1
def reset_password_voo
-
1
voo.title ||= card.i18n_signin(:forgot_password)
-
1
voo.edit_structure = [signin_field(:email)]
-
1
voo.hide :help
-
end
-
-
1
view :edit_buttons do
-
1
text = I18n.t :reset_my_password, scope: "mod.card-mod-account.set.self.signin"
-
1
button_tag text, situation: "primary", class: "_close-modal-on-success"
-
end
-
-
1
def signin_success
-
78
"REDIRECT: #{Env.interrupted_action || '*previous'}"
-
end
-
-
1
def signin_button
-
78
text = I18n.t :sign_in, scope: "mod.card-mod-account.set.self.signin"
-
78
button_tag text, situation: "primary"
-
end
-
-
1
def signup_link
-
78
text = I18n.t :or_sign_up, scope: "mod.card-mod-account.set.self.signin"
-
78
subformat(Card[:account_links]).render! :sign_up, title: text
-
end
-
-
1
def reset_password_link
-
78
text = I18n.t :reset_password, scope: "mod.card-mod-account.set.self.signin"
-
78
link = link_to_view :edit, text, path: { slot: { hide: :bridge_link } }
-
# FIXME: inline styling
-
78
raw("<div style='float:right'>#{link}</div>")
-
end
-
-
1
def edit_view_hidden
-
1
hidden_tags card: { trigger: :send_reset_password_token }
-
end
-
-
1
def edit_success
-
1
{ view: :reset_password_success }
-
end
-
-
1
def signin_field name
-
157
nest_name = "".to_name.trait(name)
-
157
[nest_name, { title: name.to_s, view: "titled",
-
nest_name: nest_name, skip_perms: true }]
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/self/signin.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Role" cards
-
#
-
1
module Role;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/type/role.rb"; end
-
1
def disabled?
-
Auth.current&.fetch(:disabled_roles)&.item_ids&.include? id
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :link_with_checkbox, cache: :never do
-
role_checkbox
-
end
-
-
1
def role_checkbox
-
name = card.disabled? ? "add_item" : "drop_item"
-
subformat(Auth.current.field(:disabled_roles, new: {})).card_form :update do
-
[check_box_tag(name, card.id, !card.disabled?, class: "_edit-item"),
-
render_link]
-
end
-
end
-
-
1
def related_by_content_items
-
super.unshift ["members", :members]
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/type/role.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Signup" cards
-
#
-
1
module Signup;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/type/signup.rb"; end
-
1
include_set Abstract::Accountable
-
-
1
require_field :account
-
-
1
def default_account_status
-
1
can_approve? ? "unverified" : "unapproved"
-
end
-
-
1
def can_approve?
-
2
Card.new(type_id: Card.default_accounted_type_id).ok? :create
-
end
-
-
1
def activate_accounted
-
1
self.type_id = Card.default_accounted_type_id
-
end
-
-
1
event :auto_approve_with_verification, :validate, on: :create, when: :can_approve? do
-
1
request_verification
-
end
-
-
1
event :approve_with_verification, :validate, on: :update, trigger: :required do
-
approvable { request_verification }
-
end
-
-
1
event :approve_without_verification, :validate, on: :update, trigger: :required do
-
# TODO: if validated here, add trigger and activate in storage phase
-
approvable do
-
activate_accounted
-
account_subfield.activate_account
-
end
-
end
-
-
1
event :act_as_current_for_integrate_stage, :integrate, on: :create do
-
# needs justification!
-
1
Auth.current_id = id
-
end
-
-
1
def account_subfield
-
1
subfield(:account) || add_subfield(:account)
-
end
-
-
1
def request_verification
-
1
acct = account_subfield
-
1
acct.add_subfield :status, content: "unverified"
-
1
acct.trigger_event! :send_verification_email
-
end
-
-
1
def approvable
-
if can_approve?
-
yield
-
else
-
abort :failure, "illegal approval" # raise permission denied?
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/type/signup.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Type; module Signup;
-
# Set: All "Signup+Views" cards (Views)
-
#
-
1
module Views;
-
1
extend Card::Set
-
2
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/type/signup/views.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def invitation?
-
2
Auth.signed_in? && card.can_approve?
-
end
-
-
1
view :new do
-
1
voo.title = invitation? ? tr(:invite) : tr(:sign_up)
-
1
super()
-
end
-
-
1
view :content_formgroups do
-
1
[account_formgroups, (card.structure ? edit_slot : "")].join
-
end
-
-
1
view :new_buttons do
-
1
button_formgroup do
-
1
[standard_create_button, invite_button].compact
-
end
-
end
-
-
1
def invite_button
-
1
return unless invitation?
-
button_tag "Send Invitation", situation: "primary"
-
end
-
-
1
view :core, template: :haml do
-
@lines = [signup_line] + account_lines
-
@body = process_content _render_raw
-
end
-
-
1
def signup_line
-
["<strong>#{safe_name}</strong>",
-
("was" if invited?),
-
"signed up on #{format_date card.created_at}"].compact.join " "
-
end
-
-
1
def invited?
-
!self_signup?
-
end
-
-
1
def self_signup?
-
card.creator_id == AnonymousID
-
end
-
-
1
def account_lines
-
if card.account
-
verification_lines
-
else
-
[tr(:missing_account)]
-
end
-
end
-
-
1
def verification_lines
-
[verification_sent_line, verification_link_line].compact
-
end
-
-
1
def verification_sent_line
-
account = card.account
-
return unless account.email_card.ok?(:read)
-
"A verification email has been sent to #{account.email}"
-
end
-
-
1
def verification_link_line
-
links = verification_links
-
return if links.empty?
-
links.join " "
-
end
-
-
1
def verification_links
-
[approve_with_token_link, approve_without_token_link, deny_link].compact
-
end
-
-
1
def approve_with_token_link
-
action = card.account.status == "unverified" ? "Resend" : "Send"
-
approval_link "#{action} verification email", :with
-
end
-
-
1
def approve_without_token_link
-
approval_link "Approve without verification", :without
-
end
-
-
1
def approval_link text, with_or_without
-
return unless card.can_approve?
-
link_to_card card, text,
-
path: { action: :update,
-
card: { trigger: "approve_#{with_or_without}_verification" } }
-
end
-
-
1
def deny_link
-
return unless card.ok? :delete
-
link_to_card card, "Deny and delete", path: { action: :delete }
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/type/signup/views.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "User" cards
-
#
-
1
module User;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/type/user.rb"; end
-
1
include Basic
-
1
include_set Abstract::Accountable
-
-
1
attr_accessor :email
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
2
view :setup, unknown: true, perms: ->(_fmt) { Auth.needs_setup? } do
-
1
with_nest_mode :edit do
-
1
voo.title = "Your deck is ready to go!" # LOCALIZE
-
1
voo.show! :help
-
1
voo.hide! :menu
-
1
voo.help = haml :setup_help
-
2
Auth.as_bot { setup_form }
-
end
-
end
-
-
1
def setup_form
-
1
frame_and_form :create do
-
[
-
1
setup_hidden_fields,
-
_render_name_formgroup,
-
account_formgroups,
-
setup_form_buttons
-
]
-
end
-
end
-
-
1
def setup_form_buttons
-
2
button_formgroup { setup_button }
-
end
-
-
1
def setup_button
-
1
submit_button text: "Set up", disable_with: "Setting up"
-
end
-
-
1
def setup_hidden_fields
-
1
hidden_tags(
-
setup: true,
-
success: "REDIRECT: #{path mark: ''}",
-
"card[type_id]" => Card.default_accounted_type_id
-
)
-
end
-
end
-
-
1
def setup?
-
3
Card::Env.params[:setup]
-
end
-
-
1
event :setup_as_bot, before: :check_permissions, on: :create, when: :setup? do
-
1
abort :failure unless Auth.needs_setup?
-
1
Auth.as_bot
-
# we need bot authority to set the initial administrator roles
-
# this is granted and inspected here as a separate event for
-
# flexibility and security when configuring initial setups
-
end
-
-
1
event :setup_first_user, :prepare_to_store, on: :create, when: :setup? do
-
1
add_subcard "signup alert email+*to", content: name
-
1
add_subfield :roles, content: roles_for_first_user
-
end
-
-
1
def roles_for_first_user
-
1
%i[help_desk shark administrator].map(&:cardname)
-
end
-
-
1
event :signin_after_setup, :integrate, on: :create, when: :setup? do
-
1
Auth.signin id
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/type/user.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class TypePlusRight; module User;
-
# Set: All "+Email" cards on "User" cards
-
#
-
# supports legacy references to <User>+*email
-
1
module Email;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/type_plus_right/user/email.rb"; end
-
# (standard representation is now <User>+*account+*email)
-
1
view :raw do
-
card.content_email || card.account_email || ""
-
end
-
-
1
def content_email
-
content if real?
-
end
-
-
1
def account_email
-
left&.account&.email
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/type_plus_right/user/email.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (AccountDropdown)
-
#
-
1
module AccountDropdown;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/navbar/set/abstract/account_dropdown.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def link_to_mycard
-
202
link_to_card Auth.current.name, nil,
-
id: "my-card-link", class: "nav-link #{classy('my-card')}"
-
end
-
-
1
def account_dropdown &render_role_item
-
202
split_button link_to_mycard, nil do
-
[
-
202
link_to_card([Auth.current, :account_settings], "Account"),
-
202
(["Roles", role_items(&render_role_item)] if special_roles?)
-
]
-
end
-
end
-
-
1
def special_roles?
-
202
Auth.current_roles.size > 1
-
end
-
-
1
def role_items
-
190
Auth.current_roles.map do |role_name|
-
626
yield role_name
-
end
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/navbar/set/abstract/account_dropdown.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Abstract; module Pointer;
-
# Set: Abstract (Pointer, HtmlViews)
-
#
-
1
module HtmlViews;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/navbar/set/abstract/pointer/html_views.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :nav_item do
-
224
nav_dropdown
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/navbar/set/abstract/pointer/html_views.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (NavbarLinks)
-
#
-
1
module NavbarLinks;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/navbar/set/all/navbar_links.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :navbar_links, perms: :none do
-
448
wrap_with :ul, class: "navbar-nav" do
-
448
navbar_items
-
end
-
end
-
-
# Iterates over all nests and links and renders them as bootstrap navbar items.
-
# Items that are pointer cards become dropdowns
-
1
def navbar_items view: :nav_item, link_class: "nav-link"
-
448
process_content nil, chunk_list: :references do |chunk|
-
1792
case chunk
-
when Card::Content::Chunk::Link
-
1792
link = chunk.render_link view: view, explicit_link_opts: { class: link_class }
-
1792
chunk.explicit_link? && view == :nav_item ? wrap_with_nav_item(link) : link
-
when Card::Content::Chunk::Nest
-
content_nest chunk.options.merge view: view
-
else
-
chunk.process_chunk
-
end
-
end
-
end
-
-
# overridden in Abstact::Pointer to render dropdown
-
1
view :nav_item do
-
224
wrap_with_nav_item link_view(class: "nav-link")
-
end
-
-
1
def wrap_with_nav_item content
-
1120
wrap_with(:li, content, class: "nav-item")
-
end
-
-
1
view :nav_link_in_dropdown do
-
448
link_to_card card, render_title, class: "dropdown-item"
-
end
-
-
1
def nav_dropdown
-
224
wrap_with(:li, class: "nav-item dropdown") do
-
[
-
224
dropdown_toggle_link,
-
dropdown_menu
-
]
-
end
-
end
-
-
1
def dropdown_toggle_link
-
224
link_to(render_title, href: "#", class: "nav-link dropdown-toggle",
-
"data-toggle": "dropdown")
-
end
-
-
1
def dropdown_menu
-
224
wrap_with :div, dropdown_menu_items, class: "dropdown-menu"
-
end
-
-
1
def dropdown_menu_items
-
224
navbar_items view: :nav_link_in_dropdown, link_class: "dropdown-item"
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/navbar/set/all/navbar_links.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+EnabledRoles" cards
-
#
-
1
module EnabledRoles;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/navbar/set/right/enabled_roles.rb"; end
-
1
include_set Abstract::AccountDropdown
-
-
1
def ok_to_read
-
true
-
end
-
-
1
def ok_to_update
-
left_id == Auth.current_id
-
end
-
-
1
def ok_to_create
-
left_id == Auth.current_id
-
end
-
-
1
def ensure_roles
-
190
self.content = Auth.current_roles.to_pointer_content if content.blank?
-
end
-
-
1
event :validate_role_enabling, :validate, on: :save do
-
illegal_roles = item_names - Auth.current_roles
-
return if illegal_roles.empty?
-
-
errors.add :content, "illegal roles: #{illegal_roles.to_sentence}" # LOCALIZE
-
end
-
-
1
event :clear_roles_cache, :prepare_to_store, before: :store_in_session do
-
clear_roles
-
Auth.update_always_cache Auth.as_id, nil
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
# permission change compared to super
-
1
view :edit_inline, perms: :none, unknown: true, cache: :never, wrap: :slot do
-
190
super()
-
end
-
-
1
def input_type
-
190
:checkbox
-
end
-
-
1
def edit_success
-
190
{ reload: true }
-
end
-
-
1
def hidden_form_tags _action, opts
-
190
"#{super} #{hidden_tags card: { type_id: SessionID }}"
-
end
-
-
1
def checkbox_input
-
190
card.ensure_roles
-
190
wrap_with :div, class: "pointer-checkbox-list" do
-
190
account_dropdown &method(:role_item_checkbox)
-
end
-
end
-
-
1
def role_item_checkbox role_name
-
626
haml :role_checkbox, id: "pointer-checkbox-#{role_name.to_name.key}",
-
checked: card.item_names.include?(role_name),
-
option_name: role_name
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/navbar/set/right/enabled_roles.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "AccountLinks"
-
#
-
1
module AccountLinks;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/navbar/set/self/account_links.rb"; end
-
1
include_set Abstract::AccountDropdown
-
-
1
def ok_to_read
-
true
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :core, cache: :never do
-
status_class = Auth.signed_in? ? "logged-in" : "logged-out"
-
wrap_with :div, id: "logging", class: status_class do
-
navbar_items.join "\n"
-
end
-
end
-
-
1
def navbar_items
-
# removed invite for now
-
links =
-
224
%i[my_card sign_out sign_up sign_in].map do |link_view|
-
896
render(link_view)
-
end.compact
-
-
224
links.map do |link|
-
896
wrap_with_nav_item link
-
end
-
end
-
-
1
def self.link_options opts={}
-
5
options = { denial: :blank, cache: :never }.merge opts
-
979
options[:perms] = ->(r) { yield r } if block_given?
-
5
options.clone
-
end
-
-
1
view :sign_up, link_options(&:show_signup_link?) do
-
32
link_to_card :signup, account_link_text(:sign_up),
-
class: nav_link_class("signup-link"),
-
path: { action: :new, mark: :signup }
-
end
-
-
225
view(:sign_in, link_options { !Auth.signed_in? }) do
-
22
link_to_card :signin, account_link_text(:sign_in),
-
class: nav_link_class("signin-link")
-
end
-
-
225
view(:sign_out, link_options { Auth.signed_in? }) do
-
202
link_to_card :signin, account_link_text(:sign_out),
-
class: nav_link_class("signout-link"),
-
path: { action: :delete }
-
end
-
-
1
view :invite, link_options(&:show_invite_link?) do
-
link_to_card :signup, account_link_text(:invite),
-
class: nav_link_class("invite-link"),
-
path: { action: :new, mark: :signup }
-
end
-
-
225
view(:my_card, link_options { Auth.signed_in? }) do
-
202
can_disable_roles? ? interactive_roles_dropdown : simple_roles_dropdown
-
end
-
-
1
def interactive_roles_dropdown
-
190
nest(enabled_roles_card,
-
view: :edit_inline, hide: %i[edit_inline_buttons name_formgroup])
-
end
-
-
1
def simple_roles_dropdown
-
12
account_dropdown(&method(:link_to_card))
-
end
-
-
1
def enabled_roles_card
-
190
Auth.current.fetch :enabled_roles, new: { type_id: SessionID }
-
end
-
-
1
def role_list
-
Auth.current_roles.map(&method(:link_to_card))
-
end
-
-
1
def can_disable_roles?
-
202
Auth.current_roles.size > 1 &&
-
Card::Codename.exists?(:enabled_roles) # workaround for broken migrations
-
end
-
-
1
def account_link_text purpose
-
256
voo.title ||
-
I18n.t(purpose, scope: "mod.card-mod-account.set.self.account_links")
-
end
-
-
1
def nav_link_class type
-
256
"nav-link #{classy(type)}"
-
end
-
-
1
def show_signup_link?
-
302
!Auth.signed_in? && Card.new(type_id: SignupID).ok?(:create)
-
end
-
-
1
def show_invite_link?
-
Auth.signed_in? &&
-
Card.new(type_id: Card.default_accounted_type_id).ok?(:create)
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/navbar/set/self/account_links.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "DropdownDivider"
-
#
-
1
module DropdownDivider;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/navbar/set/self/dropdown_divider.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :nav_item do
-
wrap_with :div, "", class: "dropdown-divider"
-
end
-
-
1
view :nav_link_in_dropdown do
-
224
wrap_with :div, "", class: "dropdown-divider"
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/navbar/set/self/dropdown_divider.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Navbox"
-
#
-
1
module Navbox;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/navbar/set/self/navbox.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :navbox, cache: :never do
-
224
select_tag "query[keyword]", "", class: "_navbox navbox form-control w-100",
-
placeholder: navbar_placeholder
-
end
-
-
1
view :navbar do
-
# FIXME: not bootstrap class here.
-
224
class_up "navbox-form", "form-inline"
-
224
render_core
-
end
-
-
1
view :core do
-
224
form_tag path(mark: :search), method: "get", role: "search",
-
class: classy("navbox-form", "nodblclick") do
-
224
wrap_with :div, class: "form-group w-100" do
-
224
render_navbox
-
end
-
end
-
end
-
-
# def initial_options
-
# return "" unless (keyword = params.dig :query, :keyword)
-
# options_for_select [keyword]
-
# end
-
-
# TODO: the more natural placeholder would be the content of the navbox card, no?
-
# Also, the forced division of "raw" and "core" should probably be replaced
-
# with a single haml template (for core view)
-
1
def navbar_placeholder
-
224
@@placeholder ||= begin
-
1
holder_card = Card["#{Card[:navbox].name}+*placeholder"]
-
1
holder_card ? holder_card.content : "Search"
-
end
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/navbar/set/self/navbox.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Html" cards
-
#
-
1
module Html;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/navbar/set/type/html.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
# deprecated; here to support old "*main menu" html cards in existing decks
-
1
view :navbar_links, perms: :none do
-
wrap_with :ul, class: "navbar-nav" do
-
item_links.map do |link|
-
wrap_with(:li, class: "nav-item") { link }
-
end.join "\n"
-
end
-
end
-
-
1
def item_links _args={}
-
raw(render_core).split(/[,\n]/)
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/navbar/set/type/html.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Bridge)
-
#
-
1
module Bridge;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge.rb"; end
-
1
BRIDGE_TABS = { "Account" => :account_tab,
-
"Guide" => :guide_tab,
-
"Engage" => :engage_tab,
-
"History" => :history_tab,
-
"Related" => :related_tab,
-
"Rules" => :rules_tab }.freeze
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
wrapper :bridge do
-
class_up "modal-dialog", "no-gaps"
-
voo.hide! :modal_footer
-
wrap_with_modal size: :full, title: bridge_breadcrumbs do
-
haml :bridge
-
end
-
end
-
-
1
def bridge_tabs
-
wrap do
-
tabs(visible_bridge_tabs, bridge_tab, load: :lazy) { _render bridge_tab }
-
end
-
end
-
-
1
def bridge_tab
-
@bridge_tab ||= bridge_param :tab
-
end
-
-
1
def bridge_param key
-
params.dig(:bridge, key)&.to_sym || try("default_bridge_#{key}")
-
end
-
-
1
def bridge_breadcrumbs
-
<<-HTML.strip_heredoc
-
<nav aria-label="breadcrumb">
-
<ol class="breadcrumb _bridge-breadcrumb">
-
<li class="breadcrumb-item">#{card.name}</li>
-
<li class="breadcrumb-item active">Edit</li>
-
</ol>
-
</nav>
-
HTML
-
end
-
-
1
def bridge_link_opts opts={}
-
opts[:"data-slot-selector"] = bridge_slot_selector
-
opts[:remote] = true
-
add_class opts, "slotter"
-
opts.bury :path, :layout, :overlay
-
opts[:path][:view] ||= :content
-
opts
-
end
-
-
1
def bridge_slot_selector
-
".bridge-main > .overlay-container > .card-slot._bottomlay-slot," \
-
".bridge-main > ._overlay-container-placeholder > .card-slot"
-
end
-
-
1
def default_bridge_tab
-
show_guide_tab? ? :guide_tab : :engage_tab
-
end
-
-
1
def breadcrumb_data title, html_class=nil
-
html_class ||= title.underscore
-
{ "data-breadcrumb": title, "data-breadcrumb-class": html_class }
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module Bridge;
-
# Set: All cards (Bridge, BridgePills)
-
#
-
1
module BridgePills;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge/bridge_pills.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
BRIDGE_PILL_UL_CLASSES =
-
1
"nav nav-pills _auto-single-select bridge-pills flex-column".freeze
-
-
1
BRIDGE_PILL_LI_CLASSES = "nav-item".freeze
-
-
1
def bridge_pills items
-
list_tag class: BRIDGE_PILL_UL_CLASSES, items: { class: BRIDGE_PILL_LI_CLASSES } do
-
items
-
end
-
end
-
-
1
def bridge_pill_items data, breadcrumb
-
data.map do |text, field, extra_opts|
-
opts = bridge_pill_item_opts breadcrumb, extra_opts, text
-
mark = opts.delete(:mark) == :absolute ? field : [card, field]
-
link_to_card mark, text, opts
-
end
-
end
-
-
1
def bridge_pill_item_opts breadcrumb, extra_opts, text
-
opts = bridge_link_opts.merge("data-toggle": "pill")
-
opts.merge! breadcrumb_data(breadcrumb)
-
-
if extra_opts
-
classes = extra_opts.delete :class
-
add_class opts, classes if classes
-
opts.deep_merge! extra_opts
-
end
-
opts["data-cy"] = "#{text.to_name.key}-pill"
-
add_class opts, "nav-link"
-
opts
-
end
-
-
1
def bridge_pill_sections tab_name
-
wrap_with :ul, class: BRIDGE_PILL_UL_CLASSES do
-
yield.map { |args| bridge_pill_section(tab_name, *args) }
-
end
-
end
-
-
1
def bridge_pill_section tab_name, title, items
-
wrap_with(:h6, title, class: "ml-1 mt-3") +
-
wrap_each_with(:li, class: BRIDGE_PILL_LI_CLASSES) do
-
bridge_pill_items(items, tab_name)
-
end.html_safe
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge/bridge_pills.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module Bridge;
-
# Set: All cards (Bridge, FollowSection)
-
#
-
1
module FollowSection;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge/follow_section.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def follow_section
-
return unless show_follow?
-
-
wrap_with :div, class: "mb-3" do
-
[follow_button_group, followers_bridge_link, follow_overview_button]
-
end
-
end
-
-
1
def follow_button_group
-
wrap_with :div, class: "btn-group btn-group-sm follow-btn-group" do
-
[follow_button, follow_advanced]
-
end
-
end
-
-
1
def follow_overview_button
-
link_to_card [Auth.current, :follow], "all followed cards",
-
bridge_link_opts(class: "btn btn-sm btn-secondary",
-
"data-cy": "follow-overview")
-
end
-
-
1
def follow_advanced
-
opts = bridge_link_opts(class: "btn btn-sm btn-primary",
-
path: { view: :overlay_rule },
-
"data-cy": "follow-advanced")
-
opts[:path].delete :layout
-
link_to_card card.follow_rule_card(Auth.current.name, new: {}),
-
icon_tag("more_horiz"), opts
-
end
-
-
1
def followers_bridge_link
-
cnt = card.followers_count
-
link_to_card card.name.field(:followers), "#{cnt} follower#{'s' unless cnt == 1}",
-
bridge_link_opts(class: "btn btn-sm ml-2 btn-secondary slotter",
-
remote: true, "data-cy": "followers")
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge/follow_section.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module Bridge;
-
# Set: All cards (Bridge, RelatedSection)
-
#
-
1
module RelatedSection;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge/related_section.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
RELATED_ITEMS =
-
{
-
1
"by name" => [["children", :children],
-
["mates", :mates]],
-
# FIXME: optimize,
-
"by content" => [["links out", :links_to],
-
["links in", :linked_to_by],
-
["nests", :nests],
-
["nested by", :nested_by],
-
["references out", :refers_to],
-
["references in", :referred_to_by]]
-
# ["by edit", [["creator", :creator],
-
# ["editors", :editors],
-
# ["last edited", :last_edited]]]
-
}.freeze
-
-
1
def related_by_name_items
-
pills = []
-
if card.name.junction?
-
pills += card.name.ancestors.map { |a| [a, a, { mark: :absolute }] }
-
end
-
pills += RELATED_ITEMS["by name"]
-
pills
-
end
-
-
1
def related_by_content_items
-
RELATED_ITEMS["by content"]
-
end
-
-
1
def related_by_type_items
-
[["#{card.type} cards", [card.type, :type, :by_name], mark: :absolute]]
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge/related_section.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module Bridge;
-
# Set: All cards (Bridge, TabViews)
-
#
-
1
module TabViews;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge/tab_views.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :engage_tab, wrap: { div: { class: "m-3 mt-4 _engage-tab" } }, cache: :never do
-
[render_follow_section, discussion_section].compact
-
end
-
-
1
view :history_tab, wrap: :slot do
-
class_up "d0-card-body", "history-slot"
-
voo.hide :act_legend
-
acts_bridge_layout card.history_acts
-
end
-
-
1
view :related_tab do
-
bridge_pill_sections "Related" do
-
%w[name content type].map do |section_name|
-
["by #{section_name}", send("related_by_#{section_name}_items")]
-
end
-
end
-
end
-
-
1
view :rules_tab, unknown: true do
-
class_up "card-slot", "flex-column"
-
wrap do
-
nest current_set_card, view: :bridge_rules_tab
-
end
-
end
-
-
1
view :follow_section, wrap: :slot, cache: :never do
-
follow_section
-
end
-
-
1
view :guide_tab, unknown: true do
-
render_guide
-
end
-
-
1
def discussion_section
-
return unless show_discussion?
-
-
field_nest(:discussion, view: :titled, title: "Discussion", show: :comment_box,
-
hide: [:menu])
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge/tab_views.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module Bridge;
-
# Set: All cards (Bridge, TabVisibility)
-
#
-
1
module TabVisibility;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge/tab_visibility.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def visible_bridge_tabs
-
Bridge::BRIDGE_TABS.select do |_title, view|
-
send "show_#{view}?"
-
end
-
end
-
-
1
private
-
-
1
def show_engage_tab?
-
return unless card.real?
-
-
show_follow? || show_discussion?
-
end
-
-
1
def show_account_tab?
-
false
-
end
-
-
1
def show_history_tab?
-
card.real?
-
end
-
-
1
def show_related_tab?
-
card.real?
-
end
-
-
1
def show_rules_tab?
-
true
-
end
-
-
1
def show_guide_tab?
-
guide.present?
-
end
-
-
1
def show_discussion?
-
d_card = discussion_card
-
return unless d_card
-
-
permission_task = d_card.new_card? ? :update : :read
-
d_card.ok? permission_task
-
end
-
-
1
def discussion_card?
-
card.junction? && card.name.tag_name.key == :discussion.cardname.key
-
end
-
-
1
def discussion_card
-
return if card.new_card? || discussion_card?
-
-
card.fetch :discussion, skip_modules: true, new: {}
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge/tab_visibility.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (EditContent)
-
#
-
1
module EditContent;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/edit_content.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :edit_form, wrap: :slot do
-
22
voo.show :edit_type_row
-
22
with_nest_mode :edit do
-
22
edit_form
-
end
-
end
-
-
1
def edit_form
-
22
voo.hide :edit_type_row
-
22
form_opts = edit_form_opts.reverse_merge success: edit_success
-
22
card_form(:update, form_opts) do
-
[
-
22
edit_view_hidden,
-
_render_edit_type_row(home_view: :edit_type_row),
-
# home_view is necessary for cancel to work correctly.
-
# it seems a little strange to have to think about home_view here,
-
# but the issue is that something currently has to happen prior to the
-
# render to get voo.slot_options to have the write home view in
-
# the slot wrap. Id think this would probably best be handled as an
-
# option to #wrap that triggers a new heir voo
-
_render_content_formgroups,
-
_render_edit_buttons
-
]
-
end
-
end
-
-
1
view :edit, perms: :update, unknown: true, cache: :never,
-
wrap: { modal: { footer: "",
-
size: :edit_modal_size,
-
title: :render_title,
-
menu: :edit_modal_menu } } do
-
22
add_name_context
-
22
with_nest_mode :edit do
-
22
voo.show :help
-
22
voo.hide :save_button
-
22
wrap true do
-
[
-
22
frame_help,
-
_render_edit_form
-
]
-
end
-
end
-
end
-
-
1
def edit_modal_size
-
22
:large
-
end
-
-
1
def edit_modal_menu
-
22
wrap_with_modal_menu do
-
22
[close_modal_window, render_bridge_link]
-
end
-
end
-
-
1
def edit_form_opts
-
# for override
-
22
{ "data-slot-selector": "modal-origin", "data-slot-error-selector": ".card-slot" }
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/edit_content.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (EditInline)
-
#
-
1
module EditInline;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/edit_inline.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :edit_inline, perms: :update, unknown: true, cache: :never, wrap: :slot do
-
190
voo.hide :name_formgroup, :type_formgroup
-
190
with_nest_mode :edit do
-
190
card_form :update, success: edit_success do
-
[
-
190
edit_view_hidden,
-
_render_content_formgroups,
-
_render_edit_inline_buttons
-
]
-
end
-
end
-
end
-
-
1
view :edit_name_row do
-
edit_row_fixed_width "Name", card.name, :name_form
-
end
-
-
1
view :edit_inline_buttons do
-
button_formgroup do
-
wrap_with "div", class: "d-flex" do
-
[standard_save_button, cancel_in_place_button, delete_button]
-
end
-
end
-
end
-
-
# TODO: better styling for this so that is reusable
-
# At the moment it is used for the name and type field in the bridge
-
# (with fixed 50px width for the title column) and
-
# for password and email for accounts (with fixed 75px width for the title column)
-
# The view is very similar to labeled but with fixed edit link on the right
-
# and a fixed width for the labels so that the content column is aligned
-
# There is also the problem that label and content are not vertically aligned
-
1
view :edit_row do
-
edit_row_fixed_width render_title, render_core, :edit_inline, 75
-
end
-
-
1
def edit_row_fixed_width title, content, edit_view, width=50
-
22
class_up "card-slot", "d-flex"
-
22
wrap do
-
22
["<label class='w-#{width}px'>#{title}</label>",
-
content,
-
edit_inline_link(edit_view, align: :right)]
-
end
-
end
-
-
1
def edit_inline_link view=:edit_inline, align: :left
-
22
align = align == :left ? "ml-2" : "ml-auto"
-
22
link_to_view view, menu_icon, class: "#{align} edit-link", "data-cy": "edit-link"
-
end
-
-
1
def cancel_in_place_button args={}
-
args.reverse_merge! class: "cancel-button btn-sm", href: path
-
cancel_button args
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/edit_inline.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (EditName)
-
#
-
1
module EditName;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/edit_name.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
# note: depends on js with selector ".edit_name-view .card-form"
-
1
view :edit_name, perms: :update do
-
frame { name_form }
-
end
-
-
# note: depends on js with selector ".name_form-view .card-form"
-
1
view :name_form, perms: :update, wrap: :slot, cache: :never do
-
name_form :edit_name_row
-
end
-
-
1
def name_form success_view=nil
-
card_form({ action: :update, id: card.id },
-
# "main-success" => "REDIRECT",
-
"data-update-origin": "true",
-
success: edit_name_success(success_view)) do
-
[hidden_edit_name_fields,
-
_render_name_formgroup,
-
rename_confirmation_alert,
-
edit_name_buttons]
-
end
-
end
-
-
1
def edit_name_success view=nil
-
success = { id: "_self" }
-
success[:view] = view if view
-
success
-
end
-
-
1
def hidden_edit_name_fields
-
hidden_tags old_name: card.name, card: { update_referers: false }
-
end
-
-
1
def edit_name_buttons
-
button_formgroup do
-
[rename_and_update_button, rename_button, standard_cancel_button]
-
end
-
end
-
-
# LOCALIZE
-
1
def rename_and_update_button
-
submit_button text: "Rename and Update", disable_with: "Renaming",
-
class: "renamer-updater"
-
end
-
-
1
def rename_button
-
button_tag "Rename", data: { disable_with: "Renaming" }, class: "renamer"
-
end
-
-
# LOCALIZE
-
1
def rename_confirmation_alert
-
msg = "<h5>Are you sure you want to rename <em>#{safe_name}</em>?</h5>"
-
msg << %(<h6>This may change names referred to by other cards.</h6>)
-
msg << %(<p>You may choose to <em>update or ignore</em> the referers.</p>)
-
msg << hidden_field_tag(:referers, 1)
-
alert("warning", false, false, class: "hidden-alert") { msg }
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/edit_name.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (EditType)
-
#
-
1
module EditType;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/edit_type.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :edit_type, cache: :never, perms: :update do
-
frame do
-
_render_edit_type_form
-
end
-
end
-
-
1
view :edit_type_form, cache: :never, perms: :update, wrap: :slot do
-
card_form :update, success: edit_type_success do
-
[type_formgroup, render_new_buttons]
-
end
-
end
-
-
1
def edit_type_success
-
{ view: :core }
-
end
-
-
1
view :edit_type_row do
-
44
return _render_bridge_type_formgroup if voo.visible?(:type_form) { false }
-
-
22
edit_row_fixed_width "Type", link_to_card(card.type), :bridge_type_formgroup
-
end
-
-
1
view :bridge_type_formgroup, unknown: true, wrap: :slot do
-
type_formgroup href: path(mark: card.id,
-
view: :edit_form,
-
assign: true,
-
slot: { show: :type_form }),
-
class: "live-type-field slotter",
-
'data-remote': true,
-
'data-slot-selector': ".card-slot.edit_form-view"
-
end
-
-
1
view :type_formgroup do
-
type_formgroup
-
end
-
-
1
def type_formgroup args={}
-
add_class args, "type-field"
-
wrap_type_formgroup do
-
type_field args
-
end
-
end
-
-
1
def wrap_type_formgroup
-
4
formgroup "Type", input: "type", class: "type-formgroup", help: false do
-
4
output [yield, hidden_field_tag(:assign, true)]
-
end
-
end
-
-
1
def type_field args={}
-
4
typelist = Auth.createable_types
-
4
current_type = type_field_current_value args, typelist
-
4
action_view.select_tag "card[type]", type_field_options(current_type),
-
args.merge("data-select2-id": "#{unique_id}-#{Time.now.to_i}")
-
end
-
-
1
def type_field_options current_type
-
4
types = grouped_types(current_type)
-
-
4
if types.size == 1
-
options_for_select types.flatten[1], current_type
-
else
-
4
grouped_options_for_select types, current_type
-
end
-
end
-
-
1
def grouped_types current_type
-
40
groups = Hash.new { |h, k| h[k] = [] }
-
4
allowed = ::Set.new Auth.createable_types
-
4
allowed << current_type if current_type
-
-
4
visible_cardtype_groups.each_pair do |name, items|
-
36
if name == "Custom"
-
4
Auth.createable_types.each do |type|
-
177
groups["Custom"] << type unless ::Card::Set::Self::Cardtype::GROUP_MAP[type]
-
end
-
else
-
32
items.each do |i|
-
148
groups[name] << i if allowed.include?(i)
-
end
-
end
-
end
-
4
groups
-
end
-
-
1
def visible_cardtype_groups
-
4
::Card::Set::Self::Cardtype::GROUP
-
end
-
-
1
def type_field_current_value args, typelist
-
4
return if args.delete :no_current_type
-
-
4
if !card.new_card? && !typelist.include?(card.type_name)
-
# current type should be an option on existing cards,
-
# regardless of create perms
-
typelist.push(card.type_name).sort!
-
end
-
4
card.type_name_or_default
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/edit_type.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Editing)
-
#
-
1
module Editing;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/editing.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
###---( TOP_LEVEL (used by menu) NEW / EDIT VIEWS )
-
1
view :bridge, perms: :update, unknown: true, cache: :never, wrap: :bridge do
-
with_nest_mode :edit do
-
add_name_context
-
voo.show :help
-
wrap true, breadcrumb_data("Editing", "edit") do
-
bridge_parts
-
end
-
end
-
end
-
-
1
view :cardboard, :bridge
-
-
1
def bridge_parts
-
voo.show! :edit_type_row
-
-
[
-
frame_help,
-
_render_edit_name_row(home_view: :edit_name_row),
-
# home_view is necessary for cancel to work correctly.
-
# it seems a little strange to have to think about home_view here,
-
# but the issue is that something currently has to happen prior to the
-
# render to get voo.slot_options to have the write home view in
-
# the slot wrap. I think this would probably best be handled as an
-
# option to #wrap that triggers a new heir voo
-
_render_edit_form
-
]
-
end
-
-
1
def edit_success
-
# for override
-
end
-
-
1
def edit_view_hidden
-
# for override
-
end
-
-
1
view :edit_buttons do
-
21
button_formgroup do
-
21
wrap_with "div", class: "d-flex" do
-
21
[standard_submit_button, edit_cancel_button, delete_button]
-
end
-
end
-
end
-
-
# TODO: add undo functionality
-
1
view :just_deleted, unknown: true do
-
wrap { "#{render_title} deleted" }
-
end
-
-
1
view :edit_rules, cache: :never, unknown: true do
-
nest current_set_card, view: :bridge_rules_tab
-
end
-
-
1
view :edit_structure, cache: :never do
-
return unless card.structure
-
-
nest card.structure_rule_card, view: :edit
-
# FIXME: this stuff:
-
# slot: {
-
# cancel_slot_selector: ".card-slot.related-view",
-
# cancel_path: card.format.path(view: :edit), hide: :edit_toolbar,
-
# hidden: { success: { view: :open, "slot[subframe]" => true } }
-
# }
-
# }
-
end
-
-
1
view :edit_nests, cache: :never do
-
frame do
-
with_nest_mode :edit do
-
multi_card_edit
-
end
-
end
-
end
-
-
# FIXME: - view can recurse. temporarily turned off
-
#
-
# view :edit_nest_rules, cache: :never do
-
# return ""#
-
# view = args[:rule_view] || :field_related_rules
-
# frame do
-
# # with_nest_mode :edit do
-
# nested_fields.map do |name, _options|
-
# nest Card.fetch(name.to_name.trait(:self)),
-
# view: :titled, title: name, rule_view: view,
-
# hide: :set_label, show: :rule_navbar
-
# end
-
# end
-
# end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/editing.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Editor)
-
#
-
1
module Editor;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/editor.rb"; end
-
-
1
Self::InputOptions.add_to_basket :options, "text area"
-
1
Self::InputOptions.add_to_basket :options, "text field"
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def input_type
-
116
voo.input_type.present? ? voo.input_type : input_type_from_rule
-
end
-
-
1
def input_type_from_rule
-
116
card.rule(:input_type)&.gsub(/[\[\]]/, "")&.tr(" ", "_")
-
end
-
-
1
def input_method input_type
-
409
"#{input_type}_input"
-
end
-
-
# core view of card is input
-
1
def input_defined_by_card
-
56
with_card input_type do |input_card|
-
nest input_card, view: :core
-
end
-
end
-
-
# move somewhere more accessible?
-
1
def with_card mark
-
56
return nil unless (card = Card[mark])
-
-
yield card
-
rescue Card::Error::CodenameNotFound
-
nil
-
end
-
-
1
view :input, unknown: true do
-
353
try(input_method(input_type)) ||
-
input_defined_by_card ||
-
send(input_method(default_input_type))
-
end
-
-
1
def default_input_type
-
56
:rich_text
-
end
-
-
1
def rich_text_input
-
56
send "#{Cardio.config.rich_text_editor || :text_area}_editor_input"
-
end
-
-
1
def text_area_input
-
1
text_area :content, rows: 5, class: "d0-card-content",
-
"data-card-type-code" => card.type_code
-
end
-
-
1
def text_field_input
-
99
text_field :content, class: classy("d0-card-content")
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/editor.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Form)
-
#
-
1
module Form;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/form.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
# FIELDSET VIEWS
-
-
# sometimes multiple card formgroups, sometimes just one
-
1
view :content_formgroups, cache: :never do
-
341
wrap_with :fieldset, edit_slot, class: classy("card-editor", "editor")
-
end
-
-
1
view :name_formgroup do
-
12
formgroup "Name", input: "name", help: false do
-
12
raw name_field
-
end
-
end
-
-
# single card content formgroup, labeled with "Content"
-
1
view :content_formgroup, unknown: true, cache: :never do
-
82
wrap_content_formgroup { content_field }
-
end
-
-
1
view :edit_in_form, cache: :never, perms: :update, unknown: true do
-
214
reset_form
-
214
@in_multi_card_editor = true
-
214
edit_slot
-
end
-
-
1
view :conflict_tracker, cache: :never, unknown: true do
-
445
return unless card&.real?
-
-
27
card.last_action_id_before_edit = card.last_action_id
-
27
hidden_field :last_action_id_before_edit, class: "current_revision_id"
-
end
-
-
1
def wrap_content_formgroup
-
41
formgroup("Content", input: :content, help: false,
-
41
class: classy("card-editor")) { yield }
-
end
-
-
1
def button_formgroup
-
150
wrap_with :div, class: classy("form-group") do
-
150
wrap_with :div, yield
-
end
-
end
-
-
1
def name_field
-
# value needed because otherwise gets wrong value if there are updates
-
12
text_field :name, value: card.name, autocomplete: "off"
-
end
-
-
1
def content_field
-
445
with_nest_mode :normal do
-
# by changing nest mode to normal, we ensure that editors (eg image
-
# previews) can render core views.
-
445
output [_render_conflict_tracker, _render_input]
-
end
-
end
-
-
# SAMPLE editor view for override
-
# view :input do
-
# text_area :content, rows: 5, class: "d0-card-content"
-
# end
-
-
1
def edit_slot
-
case
-
555
when inline_nests_editor? then _render_core
-
110
when multi_card_editor? then multi_card_edit(true)
-
214
when in_multi_card_editor? then editor_in_multi_card
-
231
else single_card_edit_field
-
end
-
end
-
-
# test: render nests within a normal rendering of the card's content?
-
# (as opposed to a standardized form)
-
1
def inline_nests_editor?
-
555
voo.input_type == :inline_nests
-
end
-
-
# test: are we opening a new multi-card form?
-
1
def multi_card_editor?
-
555
voo.structure || voo.edit_structure || # structure configured in voo
-
card.structure || # structure in card rule
-
edit_fields? # list of fields in card rule
-
end
-
-
# override and return true to optimize
-
1
def edit_fields?
-
445
edit_fields.present?
-
end
-
-
# test: are we already within a multi-card form?
-
1
def in_multi_card_editor?
-
445
@in_multi_card_editor.present?
-
end
-
-
1
def single_card_edit_field
-
231
if voo.show?(:type_formgroup) || voo.show?(:name_formgroup)
-
41
_render_content_formgroup # use formgroup for consistency
-
else
-
380
editor_wrap(:content) { content_field }
-
end
-
end
-
-
1
def editor_in_multi_card
-
214
add_junction_class
-
214
formgroup render_title,
-
input: "content", help: true, class: classy("card-editor") do
-
214
[content_field, (form.hidden_field(:type_id) if card.new_card?)]
-
end
-
end
-
-
1
def multi_card_edit fields_only=false
-
110
field_configs = edit_field_configs fields_only
-
110
return structure_link if field_configs.empty?
-
-
110
field_configs.map do |name, options|
-
214
nest name, options || {}
-
end.join "\n"
-
end
-
-
1
def structure_link
-
# LOCALIZE
-
structured = link_to_card card.structure_rule_card, "structured"
-
"<label>Content</label>"\
-
"<p><em>Uneditable; content is #{structured} without nests</em></p>"
-
end
-
-
# @param [Hash|Array] fields either an array with field names and/or field
-
# cards or a hash with the fields as keys and a hash with nest options as
-
# values
-
1
def process_edit_fields fields
-
fields.map do |field, opts|
-
field_nest field, opts
-
end.join "\n"
-
end
-
###
-
-
# If you use subfield cards to render a form for a new card
-
# then the subfield cards should be created on the new card not the existing
-
# card that build the form
-
-
1
def form
-
954
@form ||= inherit(:form) || new_form
-
end
-
-
1
def new_form
-
418
@form_root = true unless parent&.form_root
-
418
instantiate_builder(form_prefix, card, {})
-
end
-
-
1
def reset_form
-
214
@form = new_form
-
end
-
-
1
def form_prefix
-
case
-
636
when explicit_form_prefix then explicit_form_prefix # configured
-
418
when simple_form? then "card" # simple form
-
when parent.card.name == card.name then parent.form_prefix # card nests self
-
218
else edit_in_form_prefix
-
end
-
end
-
-
1
def simple_form?
-
636
form_root? || !form_root || !parent
-
end
-
-
1
def edit_in_form_prefix
-
218
"#{parent.form_prefix}[subcards][#{card.name.from form_context.card.name}]"
-
end
-
-
1
def explicit_form_prefix
-
636
inherit :explicit_form_prefix
-
end
-
-
1
def form_context
-
218
form_root? || !form_root ? self : parent
-
end
-
-
1
def form_root?
-
854
@form_root == true
-
end
-
-
1
def form_root
-
1560
return self if @form_root
-
-
838
parent ? parent.form_root : nil
-
end
-
-
1
def card_form action, opts={}
-
341
@form_root = true
-
341
hidden = hidden_form_tags action, opts
-
341
form_for card, card_form_opts(action, opts) do |cform|
-
341
@form = cform
-
341
hidden + output(yield(cform))
-
end
-
end
-
-
1
def hidden_form_tags _action, opts
-
341
success = opts.delete :success
-
341
success_tags success
-
end
-
-
# @param action [Symbol] :create or :update
-
# @param opts [Hash] html options
-
# @option opts [Boolean] :redirect (false) if true form is no "slotter"
-
1
def card_form_opts action, opts={}
-
341
url, action = card_form_url_and_action action
-
341
html_opts = card_form_html_opts action, opts
-
341
form_opts = { url: url, html: html_opts }
-
341
form_opts[:remote] = true unless html_opts.delete(:redirect)
-
341
form_opts
-
end
-
-
1
def card_form_html_opts action, opts={}
-
341
add_class opts, "card-form"
-
341
add_class opts, "slotter" unless opts[:redirect] || opts[:no_slotter]
-
341
add_class opts, "autosave" if action == :update
-
341
opts
-
end
-
-
1
def card_form_url_and_action action
-
341
case action
-
341
when Symbol then [path(action: action), action]
-
when Hash then [path(action), action[:action]]
-
# for when non-action path args are required
-
else
-
raise Card::Error, "unsupported #card_form_url action: #{action}"
-
end
-
end
-
-
1
def editor_wrap type=nil
-
461
html_class = "editor"
-
461
html_class << " #{type}-editor" if type
-
461
wrap_with :div, class: html_class do
-
461
yield
-
end
-
end
-
-
# FIELD VIEWS
-
-
1
def add_junction_class
-
214
return unless card.name.junction?
-
-
214
class_up "card-editor", "RIGHT-#{card.name.tag_name.safe_key}"
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/form.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (FormButtons)
-
#
-
1
module FormButtons;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/form_buttons.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def standard_submit_button
-
21
output [standard_save_button, standard_save_and_close_button]
-
end
-
-
1
def standard_save_button opts={}
-
21
return if voo&.hide?(:save_button)
-
add_class opts, "submit-button btn-sm mr-3 _update-history-pills"
-
opts[:text] ||= "Save"
-
opts["data-cy"] = "save"
-
submit_button opts
-
end
-
-
# @param opts [Hash]
-
# @option close [:modal, :overlay]
-
#
-
1
def standard_save_and_close_button opts={}
-
21
close = opts.delete(:close) || :modal
-
21
text = opts[:text] || "Save and Close"
-
21
add_class opts, "submit-button btn-sm mr-3 _close-on-success"
-
21
add_class opts, "_update-origin" unless opts[:no_origin_update]
-
21
opts.reverse_merge! text: text, "data-cy": "submit-#{close}"
-
-
21
submit_button opts
-
end
-
-
1
def standard_cancel_button args={}
-
49
args.reverse_merge! class: "cancel-button ml-4", href: path, "data-cy": "cancel"
-
49
cancel_button args
-
end
-
-
1
def modal_cancel_button
-
21
modal_close_button "Cancel", situation: "secondary", class: "btn-sm cancel-button"
-
end
-
-
1
def edit_cancel_button
-
21
modal_cancel_button
-
end
-
-
1
def new_cancel_button
-
voo.show?(:cancel_button) && modal_cancel_button
-
end
-
-
1
def delete_button opts={}
-
21
link_to "Delete", delete_button_opts(opts)
-
end
-
-
1
def delete_button_opts opts={}
-
21
add_class opts, "slotter btn btn-outline-danger ml-auto btn-sm"
-
21
opts["data-confirm"] = delete_confirm opts
-
21
opts[:path] = { action: :delete }
-
21
opts[:path][:success] = delete_success(opts) unless opts.delete(:no_success)
-
21
opts[:remote] = true
-
21
opts
-
end
-
-
1
def delete_confirm opts
-
19
opts.delete(:confirm) || "Are you sure you want to delete #{safe_name}?"
-
end
-
-
1
def delete_success opts
-
21
opts.delete(:success) || (main? ? "REDIRECT: *previous" : { view: :just_deleted })
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/form_buttons.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (FormElements)
-
#
-
1
module FormElements;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/form_elements.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def success_tags opts
-
341
return "" unless opts.present?
-
-
319
hidden_tags success: opts
-
end
-
-
# convert hash into a collection of hidden tags
-
1
def hidden_tags hash, base=nil
-
893
hash ||= {}
-
893
hash.inject("") do |result, (key, val)|
-
895
new_base = base ? "#{base}[#{key}]" : key
-
895
result + process_hidden_value(val, new_base)
-
end
-
end
-
-
1
def process_hidden_value val, base
-
895
case val
-
when Hash
-
382
hidden_tags val, base
-
when Array
-
base += "[]"
-
val.map do |v|
-
hidden_field_tag base, v
-
end.join
-
else
-
513
hidden_field_tag base, val
-
end
-
end
-
-
FIELD_HELPERS =
-
%w[
-
1
hidden_field color_field date_field datetime_field datetime_local_field
-
email_field month_field number_field password_field phone_field
-
range_field search_field telephone_field text_area text_field time_field
-
url_field week_field file_field label check_box radio_button
-
].freeze
-
-
1
FIELD_HELPERS.each do |method_name|
-
22
define_method(method_name) do |*args|
-
1
form.send(method_name, *args)
-
end
-
end
-
-
1
def submit_button args={}
-
72
text = args.delete(:text) || "Submit"
-
72
args.reverse_merge! situation: "primary", data: {}
-
72
args[:data][:disable_with] ||= args.delete(:disable_with) || "Submitting"
-
72
button_tag text, args
-
end
-
-
# redirect to *previous if no :href is given
-
1
def cancel_button args={}
-
49
return unless voo.show? :cancel_button
-
49
text = args.delete(:text) || "Cancel"
-
49
add_class args, "btn btn-#{args.delete(:situation) || 'secondary'}"
-
49
add_class args, cancel_strategy(args[:redirect], args[:href])
-
49
args[:href] ||= path_to_previous
-
49
args["data-remote"] = true
-
49
link_to text, args
-
end
-
-
1
def cancel_strategy redirect, href
-
49
redirect = href.blank? if redirect.nil?
-
49
redirect ? "redirecter" : "slotter"
-
end
-
-
1
def path_to_previous
-
49
path mark: "*previous"
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/form_elements.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Formgroup)
-
#
-
1
module Formgroup;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/formgroup.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
# a formgroup has a label, an input and help text
-
1
def formgroup title, opts={}, &block
-
271
wrap_with :div, formgroup_div_args(opts[:class]) do
-
271
formgroup_body title, opts, &block
-
end
-
end
-
-
1
def formgroup_body title, opts, &block
-
271
label = formgroup_label opts[:input], title
-
271
editor_body = editor_wrap opts[:input], &block
-
271
help_text = formgroup_help_text opts[:help]
-
271
"#{label}<div>#{help_text} #{editor_body}</div>"
-
end
-
-
1
def formgroup_label input, title
-
271
return if voo&.hide?(:title) || title.blank?
-
-
271
label_type = input || :content
-
271
form.label label_type, title
-
end
-
-
1
def formgroup_div_args html_class
-
271
div_args = { class: ["form-group", html_class].compact.join(" ") }
-
271
div_args[:card_id] = card.id if card.real?
-
271
div_args[:card_name] = h card.name if card.name.present?
-
271
div_args
-
end
-
-
1
def formgroup_help_text text=nil
-
271
return "" if text == false
-
-
214
class_up "help-text", "help-block"
-
214
voo.help = text if voo && text.to_s != "true"
-
214
_render_help
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/formgroup.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (New)
-
#
-
1
module New;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/new.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :new, perms: :create, unknown: true, cache: :never do
-
50
new_view_frame_and_form new_form_opts
-
end
-
-
1
view :new_content_form, wrap: :slot, unknown: true, cache: :never do
-
with_nest_mode :edit do
-
create_form
-
end
-
end
-
-
1
view :new_in_modal, perms: :create, unknown: true, cache: :never,
-
wrap: { modal: { footer: "", size: :edit_modal_size,
-
title: :new_in_modal_title,
-
menu: :new_modal_menu } } do
-
_render_new_content_form
-
end
-
-
1
def create_form
-
form_opts = new_in_modal_form_opts.reverse_merge(success: new_in_modal_success)
-
buttons = form_opts.delete(:buttons) || _render_new_buttons
-
-
voo.title ||= new_view_title if new_name_prompt?
-
voo.show :help
-
card_form(:create, form_opts) do
-
create_form_with_alert_guide buttons
-
end
-
end
-
-
1
def new_modal_size
-
:large
-
end
-
-
1
def new_modal_menu
-
wrap_with_modal_menu do
-
[close_modal_window, render_bridge_link]
-
end
-
end
-
-
1
def new_view_frame_and_form form_opts={}
-
50
buttons = form_opts.delete(:buttons) || _render_new_buttons
-
50
form_opts = form_opts.reverse_merge(success: new_success)
-
-
50
with_nest_mode :edit do
-
50
voo.title ||= new_view_title if new_name_prompt?
-
50
voo.show :help
-
50
frame_and_form :create, form_opts do
-
50
create_form_with_alert_guide buttons
-
end
-
end
-
end
-
-
1
def create_form_with_alert_guide buttons
-
50
wrap_with :div, class: "d-flex justify-content-between" do
-
50
[(wrap_with(:div, class: "w-100") do
-
[
-
50
new_view_hidden,
-
new_view_name,
-
new_view_type,
-
_render_content_formgroups,
-
buttons
-
]
-
end),
-
50
(alert_guide if voo.show?(:guide))]
-
end
-
end
-
-
1
def new_view_hidden; end
-
-
1
def new_in_modal_form_opts
-
{ "data-slot-selector": "modal-origin", "data-slot-error-selector": ".card-slot",
-
buttons: _render_new_in_modal_buttons }
-
end
-
-
1
def new_form_opts
-
50
{ "main-success" => "REDIRECT" }
-
end
-
-
1
def new_view_title
-
10
output(
-
"New",
-
10
(card.type_name unless card.type_id == Card.default_type_id)
-
)
-
end
-
-
1
def new_in_modal_title
-
new_name_prompt? ? new_view_title : render_title
-
end
-
-
1
def new_success
-
50
card.rule(:thanks) || "_self"
-
end
-
-
1
def new_in_modal_success; end
-
-
# NAME HANDLING
-
-
1
def new_view_name
-
50
if new_name_prompt?
-
11
new_name_formgroup
-
39
elsif !autoname?
-
39
hidden_field_tag "card[name]", card.name
-
end
-
end
-
-
1
def new_name_formgroup
-
11
output _render_name_formgroup,
-
hidden_field_tag("name_prompt", true)
-
end
-
-
1
def new_name_prompt?
-
100
voo.visible? :name_formgroup do
-
50
needs_name? || params[:name_prompt]
-
end
-
end
-
-
1
def autoname?
-
50
@autoname.nil? ? (@autoname = card.rule_card :autoname).present? : @autoname
-
end
-
-
1
def needs_name?
-
50
card.name.blank? && !autoname?
-
end
-
-
# TYPE HANDLING
-
-
1
def new_view_type
-
50
if new_type_prompt?
-
4
_render_new_type_formgroup
-
else
-
46
hidden_field_tag "card[type_id]", card.type_id
-
end
-
end
-
-
1
def new_type_prompt?
-
50
voo.visible? :new_type_formgroup do
-
50
!new_type_preset? && new_type_prompt_context? && new_type_permitted?
-
end
-
end
-
-
1
def new_type_preset?
-
50
params[:type] || voo.type
-
end
-
-
1
def new_type_prompt_context?
-
4
main? || card.simple? || card.is_template?
-
end
-
-
1
def new_type_permitted?
-
4
Card.new(type_id: card.type_id).ok? :create
-
end
-
-
1
view :new_type_formgroup do
-
4
wrap_type_formgroup do
-
4
type_field class: "type-field live-type-field",
-
href: path(view: :new),
-
"data-remote" => true
-
end
-
end
-
-
1
view :new_buttons do
-
49
button_formgroup do
-
49
[standard_create_button, standard_cancel_button(cancel_button_new_args)]
-
end
-
end
-
-
1
view :new_in_modal_buttons do
-
button_formgroup do
-
wrap_with "div", class: "d-flex" do
-
[standard_save_and_close_button(text: "Submit"), modal_cancel_button]
-
end
-
end
-
end
-
-
# path to redirect to after canceling a new form
-
1
def cancel_button_new_args
-
href = case
-
98
when main? then path_to_previous
-
when voo&.home_view then path(view: voo.home_view)
-
else path(view: :unknown)
-
end
-
49
{ href: href }
-
end
-
-
1
def standard_create_button
-
50
submit_button class: "submit-button create-submit-button"
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/new.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (OverlayGuide)
-
#
-
1
module OverlayGuide;
-
1
extend Card::Set
-
2
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/overlay_guide.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :overlay_guide,
-
cache: :never, unknown: true, template: :haml,
-
wrap: { slot: { class: "_overlay d0-card-overlay card nodblclick" } } do
-
# TODO: use a common template for this and the nest editor
-
# (the common thing is that they both are an overlay of the bridge sidebar)
-
# and maybe make it look more like the overlay on the left with the same close icon
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/overlay_guide.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (TemplateNest)
-
#
-
1
module TemplateNest;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/template_nest.rb"; end
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
view :template_nest, cache: :never, unknown: true do
-
21
return "" unless voo.nest_name
-
-
21
if voo.nest_name.to_name.field_only?
-
21
with_nest_mode :normal do
-
21
nest template_link_set_name, view: :template_link
-
end
-
else
-
"{{#{voo.nest_syntax}}}"
-
end
-
end
-
-
1
def template_link_set_name
-
21
name = voo.nest_name.to_name
-
21
if name.absolute?
-
name.trait_name :self
-
else
-
21
template_link_set_name_for_relative_name name
-
end
-
end
-
-
1
def template_link_set_name_for_relative_name name
-
21
name = name.stripped.gsub(/^\+/, "")
-
-
21
if (type = on_type_set)
-
21
[type, name].to_name.trait_name :type_plus_right
-
else
-
name.to_name.trait_name :right
-
end
-
end
-
-
1
def on_type_set
-
return unless
-
21
(tmpl_set_name = parent.card.name.trunk_name) &&
-
21
(tmpl_set_class_name = tmpl_set_name.tag_name) &&
-
21
(tmpl_set_class_card = Card[tmpl_set_class_name]) &&
-
21
(tmpl_set_class_card.codename == :type)
-
-
21
tmpl_set_name.left_name
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/template_nest.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "List" cards
-
#
-
1
module List;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/type/list.rb"; end
-
1
def input_type_content_options
-
["multiselect", "checkbox", "autocompleted list", "filtered list"]
-
end
-
-
1
def show_content_options?
-
true
-
end
-
-
1
def show_input_type?
-
true
-
end
-
-
1
def field_settings
-
%i[default help input_type content_options content_option_view]
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/type/list.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "PlainText" cards
-
#
-
1
module PlainText;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/type/plain_text.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def input_type_content_options
-
["text area", "text field", "ace editor"]
-
end
-
end
-
-
1
def field_settings
-
%i[default help input_type]
-
end
-
-
1
def show_input_type?
-
true
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/type/plain_text.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Pointer" cards
-
#
-
1
module Pointer;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/type/pointer.rb"; end
-
1
def show_content_options?
-
true
-
end
-
-
1
def show_input_type?
-
true
-
end
-
-
1
def input_type_content_options
-
%w[select radio autocomplete]
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/type/pointer.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (AceEditor)
-
#
-
1
module AceEditor;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-ace_editor/set/all/ace_editor.rb"; end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def ace_editor_input
-
3
text_area :content, rows: 5,
-
class: "d0-card-content ace-editor-textarea",
-
"data-ace-mode" => ace_mode
-
end
-
-
1
def ace_mode
-
3
:html
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-ace_editor/set/all/ace_editor.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Ace"
-
#
-
1
module Ace;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-ace_editor/set/self/ace.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def raw_help_text
-
"Configure [[https://ace.c9.io/|ace]], "\
-
"Decko's default code editor, using these available "\
-
"[[https://github.com/ajaxorg/ace/wiki/Configuring-Ace|options]]."
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-ace_editor/set/self/ace.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "ScriptAce"
-
#
-
1
module ScriptAce;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-ace_editor/set/self/script_ace.rb"; end
-
1
include_set Abstract::CodeFile
-
-
1
Self::ScriptLibraries.add_item :script_ace
-
1
Self::InputOptions.add_to_basket :options, "ace editor"
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-ace_editor/set/self/script_ace.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "ScriptAceConfig"
-
#
-
1
module ScriptAceConfig;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-ace_editor/set/self/script_ace_config.rb"; end
-
1
include_set Abstract::CodeFile
-
-
1
Self::ScriptEditors.add_item :script_ace_config
-
1
All::Head::HtmlFormat.add_to_basket :mod_js_config, [:ace, "setAceConfig"]
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-ace_editor/set/self/script_ace_config.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (Media)
-
#
-
1
module Media;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bar_and_box/set/abstract/media.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def image_card
-
7
@image_card ||= card.fetch(:image, new: {})
-
end
-
-
1
def text_with_image opts={}
-
7
class_up "media-left", "m-2"
-
7
@image_card = Card.cardish(opts[:image]) if opts[:image]
-
7
haml :media_snippet, normalized_text_with_image_opts(opts)
-
end
-
-
1
private
-
-
1
def normalized_text_with_image_opts opts
-
7
opts.reverse_merge! title: _render_title, text: "", size: voo.size, media_opts: {}
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bar_and_box/set/abstract/media.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Bar)
-
#
-
1
module Bar;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bar_and_box/set/all/bar.rb"; end
-
1
include_set Abstract::BsBadge
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
setting :bar_cols
-
1
setting :info_bar_cols
-
-
1
view :info_bar do
-
render_bar show: :bar_middle
-
end
-
-
1
before :bar do
-
56
class_up "bar", card.safe_set_keys
-
56
voo.hide! :bar_collapse_link
-
56
voo.hide :edit_link, :full_page_link, :bridge_link
-
end
-
-
1
view :bar, unknown: :unknown_bar do
-
56
voo.hide :bar_middle
-
56
voo.hide :bar_bottom # needed for toggle
-
56
class_up_bar_sides(voo.show?(:bar_middle))
-
# note: above cannot be in `before`, because before blocks run before viz processing
-
112
wrap { haml :bar }
-
end
-
-
1
bar_cols 9, 3
-
1
info_bar_cols 5, 4, 3
-
-
1
view :unknown_bar, unknown: true do
-
voo.hide! :bar_middle, :bar_bottom, :bar_nav
-
wrap { haml :bar }
-
end
-
-
1
before :expanded_bar do
-
class_up "bar", card.safe_set_keys
-
voo.hide! :bar_expand_link
-
end
-
-
1
view :expanded_bar do
-
class_up_bar_sides(false)
-
wrap { haml :expanded_bar }
-
end
-
-
1
def class_up_bar_sides middle
-
56
class_up_cols %w[bar-left bar-right], bar_cols
-
56
class_up_cols %w[bar-left bar-middle bar-right], info_bar_cols, "md" if middle
-
end
-
-
1
def class_up_cols classes, cols, context=nil
-
56
classes.each_with_index do |cls, i|
-
112
class_up cls, ["col", context, cols[i]].compact.join("-")
-
end
-
end
-
-
1
view :bar_left do
-
49
bar_title
-
end
-
-
1
def bar_title
-
49
return render_missing if card.unknown?
-
-
49
if voo.show?(:toggle)
-
49
link_to_view bar_title_toggle_view, render_title
-
else
-
render_title
-
end
-
end
-
-
1
def bar_title_toggle_view
-
49
voo.show?(:bar_bottom) ? :bar : :expanded_bar
-
end
-
-
1
view :bar_right, unknown: :blank do
-
48
[(render(:short_content) unless voo.show?(:bar_middle)),
-
render(:edit_button, optional: :hide)]
-
end
-
-
1
view :bar_middle, unknown: :blank do
-
render :short_content
-
end
-
-
1
view :bar_bottom do
-
render(nest_mode == :edit ? :edit : :core)
-
end
-
-
1
view :bar_nav, unknown: true, wrap: { div: { class: "bar-nav" } } do
-
56
[render_bar_expand_link,
-
render_bar_collapse_link,
-
render_full_page_link,
-
render_edit_link,
-
render_bridge_link]
-
end
-
-
1
view :bar_expand_link, unknown: true do
-
56
link_to_view :expanded_bar, icon_tag(:keyboard_arrow_down)
-
end
-
-
1
view :bar_collapse_link, unknown: true do
-
link_to_view :bar, icon_tag(:keyboard_arrow_up)
-
end
-
-
1
view :edit_button do
-
view = voo.edit == :inline ? :edit_inline : :edit
-
link_to_view view, "Edit", class: "btn btn-sm btn-outline-primary mr-2"
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bar_and_box/set/all/bar.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Box)
-
#
-
1
module Box;
-
1
extend Card::Set
-
2
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bar_and_box/set/all/box.rb"; end
-
1
view :box, template: :haml do
-
voo.hide :menu
-
end
-
-
1
view :box_top do
-
render_title_link
-
end
-
-
1
view :box_middle do
-
_render_content
-
end
-
-
1
view :box_bottom do
-
[_render_creator_credit,
-
_render_updated_by]
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bar_and_box/set/all/box.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "StyleMedia"
-
#
-
1
module StyleMedia;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bar_and_box/set/self/style_media.rb"; end
-
1
include_set Abstract::CodeFile
-
1
Self::StyleMods.add_item :style_media
-
-
1
def source_files
-
scss_files [:image_box]
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bar_and_box/set/self/style_media.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Image" cards
-
#
-
1
module Image;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bar_and_box/set/type/image.rb"; end
-
IMAGE_BOX_SIZE_MAP = {
-
1
icon: :icon, small: :small, medium: :small, large: :medium, xlarge: :medium
-
}.freeze
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :boxed, unknown: true do
-
14
image_box { |size| render_core size: size }
-
end
-
-
1
view :boxed_link, unknown: true do
-
image_box { |size| link_to_card image_box_link_target, render_core(size: size) }
-
end
-
-
1
def image_box
-
7
voo.size ||= :medium
-
7
wrap_with :div, title: image_box_title, class: "image-box #{voo.size}" do
-
7
yield image_box_size
-
end
-
end
-
-
## METHODS FOR OVERRIDE
-
-
1
def image_box_size
-
7
IMAGE_BOX_SIZE_MAP[voo.size.to_sym] || :medium
-
end
-
-
1
def image_box_card_name
-
7
card.name.junction? ? card.name.left : card.name
-
end
-
-
1
def image_box_link_target
-
image_box_card_name
-
end
-
-
1
def image_box_title
-
7
voo.title || image_box_card_name
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bar_and_box/set/type/image.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (BootstrapCodeFile)
-
#
-
1
module BootstrapCodeFile;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/abstract/bootstrap_code_file.rb"; end
-
-
1
def self.included host_class
-
3
host_class.include_set Abstract::CodeFile
-
3
host_class.include OverrideCodeFile
-
end
-
-
1
module OverrideCodeFile
-
1
def content
-
stylesheets.join "\n"
-
end
-
-
1
def stylesheets
-
load_stylesheets unless @stylesheets
-
@stylesheets
-
end
-
-
1
def add_bs_subdir sub_dir
-
Dir.glob("#{bootstrap_path}/#{sub_dir}/*.scss").each do |path|
-
load_from_path path
-
end
-
end
-
-
1
def mod_path
-
mod_root :bootstrap
-
end
-
-
1
def bootstrap_path
-
"#{mod_path}/vendor/bootstrap/scss"
-
end
-
-
1
def add_stylesheet filename, type: :scss
-
load_from_path "#{mod_path}/lib/stylesheets/#{filename}.#{type}"
-
end
-
-
1
def add_stylesheet_file path
-
load_from_path File.join(mod_path, path)
-
end
-
-
1
def add_bs_stylesheet filename, type: :scss, subdir: nil
-
path = File.join(*[bootstrap_path, subdir, "_#{filename}.#{type}"].compact)
-
load_from_path path
-
end
-
-
1
def load_from_path path
-
@stylesheets ||= []
-
Rails.logger.info "reading file: #{path}"
-
@stylesheets << File.read(path)
-
end
-
-
1
def source_changed _since:
-
false
-
end
-
-
1
def existing_source_paths
-
[]
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/abstract/bootstrap_code_file.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (BootswatchTheme)
-
#
-
# Bootswatch themes are free themes for bootstrap available at https://bootswatch.com/.
-
# For every bootswatch theme we have one card of card type "bootswatch skin".
-
# They all have codenames following the pattern "#{theme_name}_skin".
-
#
-
# The original bootswatch theme is build from two files, `_variables.scss` and
-
# `_bootswatch.scss`. The original bootstrap scss has to be put between those two.
-
# `_variables.scss` overrides bootstrap variables, `_bootswatch.scss` overrides
-
# bootstrap css (variables are defined with `!default` hence only the first appearance
-
# has an effect, for css the last appearance counts)
-
#
-
# The content of a bootswatch theme card consists of four parts:
-
# * pre_variables: hard-coded theme independent stuff
-
# and bootstrap functions to make them available in the variables part
-
# * variables: the content from `_variables.scss`,
-
# * post_variables: the bootstrap css and libraries like select2 and
-
# bootstrap-colorpicker that depend on the theme
-
# * stylesheets: the content from `_bootswatch.scss` and custom styles
-
#
-
# For the original bootswatch themes all those parts are hard-coded and the content
-
1
module BootswatchTheme;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/abstract/bootswatch_theme.rb"; end
-
# is taken from files.
-
# The bootswatch theme content is taken directly from the files in the bootswatch
-
# submodule. For the rest we use code file cards.
-
# Cards of type "customized bootswatch skin" have the same structure but make the variables
-
# and stylesheets part editable.
-
#
-
# Bootswatch theme cards are machine cards for the following reason.
-
# Machine cards usually store all involved input cards of all nested levels in
-
# there +*machine_input pointer. All those input cards
-
# are processed separately and the result is joined to build the machine output.
-
# That's a problem for this card when it's used as input.
-
# A lot of the items depend on the variables scss and can't
-
# be processed independently. Therefore we return only self as item card and join
-
# the content of all the item cards in the content of the bootswatch theme card.
-
# But then this card has to forward updates of its items to the machine cards it provides
-
# input for.
-
# To do that it is a machine itself and stores the generated machine output as its
-
# content which will trigger the updates of other machines that use this card.
-
-
1
include_set Abstract::Machine
-
1
include_set Type::Scss
-
1
include_set Abstract::CodeFile
-
1
include_set Abstract::SkinBox
-
-
1
CONTENT_PARTS = %i[pre_variables variables post_variables stylesheets].freeze
-
-
1
PRE_VARIABLES_CARD_NAMES = %i[
-
style_jquery_ui_smoothness
-
bootstrap_functions
-
].freeze
-
-
1
POST_VARIABLES_CARD_NAMES = %i[
-
bootstrap_core
-
style_cards
-
style_bootstrap_cards
-
style_libraries
-
style_mods
-
].freeze
-
-
# @return [Array<Card::Name,String>]
-
1
def variables_card_names
-
[]
-
end
-
-
# @return [Array<Card::Name,String>]
-
1
def stylesheets_card_names
-
[]
-
end
-
-
# reject cards that don't contribute directly to the content like skin or pointer cards
-
1
def engine_input
-
extended_input_cards.select { |ca| ca.type_id.in? [Card::ScssID, Card::CssID] }
-
end
-
-
# Don't create "+*machine output" file card
-
# instead save the the output as the card's content is
-
1
def after_engine output
-
Card::Auth.as_bot { update! db_content: output }
-
end
-
-
# needed to make the refresh_script_and_style method work with these cards
-
1
def source_files
-
extended_input_cards.map do |i_card|
-
i_card.try(:source_files)
-
end.flatten.compact
-
end
-
-
# needed to make the refresh_script_and_style method work with these cards
-
1
def existing_source_paths
-
extended_input_cards.map do |i_card|
-
i_card.try(:existing_source_paths)
-
end.flatten.compact
-
end
-
-
1
def extended_input_cards
-
input_names.map do |n|
-
Card.fetch(n).extended_item_cards
-
end.flatten.compact
-
end
-
-
1
def content
-
CONTENT_PARTS.map do |n|
-
send "#{n}_content"
-
end.join "\n"
-
end
-
-
1
def pre_variables_content
-
load_content(*PRE_VARIABLES_CARD_NAMES)
-
end
-
-
1
def variables_content
-
load_content variables_card_names
-
end
-
-
1
def post_variables_content
-
load_content(*POST_VARIABLES_CARD_NAMES)
-
end
-
-
1
def stylesheets_content
-
load_content stylesheets_card_names
-
end
-
-
1
def input_names _args={}
-
(PRE_VARIABLES_CARD_NAMES + variables_card_names +
-
POST_VARIABLES_CARD_NAMES + stylesheets_card_names).compact.map do |n|
-
Card.fetch_name(n)
-
end.compact
-
end
-
-
1
def item_names _args={}
-
[]
-
end
-
-
1
def item_cards _args={}
-
[self]
-
end
-
-
1
def load_content *names
-
cards = names.flatten.map { |n| Card.fetch(n)&.extended_item_cards }
-
cards.flatten.compact.map(&:content).join "\n"
-
end
-
-
1
def scss_from_theme_file file
-
return "" unless (path = ::File.join(source_dir, "_#{file}.scss")) &&
-
::File.exist?(path)
-
::File.read path
-
end
-
-
1
def theme_name
-
/^(.+)_skin$/.match(codename)&.capture(0) ||
-
/^(.+)[ _][sS]kin/.match(name)&.capture(0)&.downcase
-
end
-
-
1
def source_dir
-
@source_dir ||= File.expand_path(
-
"#{mod_root :bootstrap}/vendor/bootswatch/dist/#{theme_name}", __FILE__
-
)
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/abstract/bootswatch_theme.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Abstract; module BootswatchTheme;
-
# Set: Abstract (BootswatchTheme, HtmlViews)
-
#
-
1
module HtmlViews;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/abstract/bootswatch_theme/html_views.rb"; end
-
1
include_set Abstract::Media
-
1
include_set Abstract::BsBadge
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
before :box do
-
voo.show! :customize_button, :box_middle
-
end
-
-
1
view :one_line_content do
-
""
-
end
-
-
1
view :bar_left do
-
7
class_up "card-title", "my-0 ml-2"
-
7
class_up "media-left", "m-0"
-
7
text_with_image size: :medium, title: "", text: _render_title,
-
media_opts: { class: "align-items-center" }
-
# field_nest(:image, view: :core) + wrap_with(:h4, render(:title))
-
end
-
-
1
view :bar_right do
-
7
customize_button text: "Customize"
-
end
-
-
1
view :bar_bottom do
-
wrap_with :code do
-
render_core
-
end
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/abstract/bootswatch_theme/html_views.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module Bootstrap;
-
# Set: All cards (Bootstrap, Accordion)
-
#
-
1
module Accordion;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/accordion.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def accordion_group list, collapse_id=nil, args={}
-
collapse_id ||= card.name.safe_key
-
accordions = ""
-
index = 1
-
case list
-
when Array then accordions = list.join
-
when String then accordions = list
-
else
-
list.each_pair do |title, content|
-
accordions << accordion(title, content, "#{collapse_id}-#{index}")
-
index += 1
-
end
-
end
-
add_class args, "act-accordion-group w-100"
-
wrap_with :div, class: args[:class], id: "accordion-#{collapse_id}",
-
role: "tablist", "aria-multiselectable" => "true" do
-
accordions
-
end
-
end
-
-
1
def accordion title, content, collapse_id=card.name.safe_key
-
accordion_content =
-
case content
-
when Hash then accordion_group content, collapse_id
-
when Array then content.present? && list_group(content)
-
when String then content
-
end
-
<<-HTML.html_safe
-
<div class="card">
-
#{accordion_panel(title, accordion_content, collapse_id)}
-
</div>
-
HTML
-
end
-
-
1
def accordion_panel title, body, collapse_id, _panel_heading_link=false
-
if body
-
<<-HTML
-
<div class="card-header" role="tab" id="heading-#{collapse_id}">
-
<h5 class="mb-0">
-
<a data-toggle="collapse" data-parent="#accordion-#{collapse_id}" \
-
href="##{collapse_id}" aria-expanded="true" \
-
aria-controls="#{collapse_id}">
-
#{title}
-
</a>
-
</h5>
-
</div>
-
<div id="#{collapse_id}" class="collapse" \
-
role="tabpanel" aria-labelledby="heading-#{collapse_id}">
-
<div class="card-body">
-
#{body}
-
</div>
-
</div>
-
HTML
-
else
-
<<-HTML
-
<li class="list-group-item">
-
<h4 class="card-header">#{title}</h4>
-
</li>
-
HTML
-
end
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/accordion.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module Bootstrap;
-
# Set: All cards (Bootstrap, Dropdown)
-
#
-
1
module Dropdown;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/dropdown.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def dropdown_button name, items_or_opts={}, opts={}
-
items = block_given? ? yield : items_or_opts
-
opts = items_or_opts if block_given?
-
<<-HTML
-
<div class="btn-group #{opts[:extra_css_class]}" role="group">
-
<button class="btn btn-primary dropdown-toggle"
-
data-toggle="dropdown" title="#{name}" aria-expanded="false"
-
aria-haspopup="true">
-
#{icon_tag opts[:icon] if opts[:icon]} #{name}
-
<span class="caret"></span>
-
</button>
-
#{dropdown_list items, opts[:class], opts[:active]}
-
</div>
-
HTML
-
end
-
-
1
def split_button main_button, active_item
-
202
wrap_with :div, class: "btn-group" do
-
[
-
202
main_button,
-
split_button_toggle,
-
dropdown_list(yield, "dropdown-menu-right", active_item)
-
]
-
end
-
end
-
-
1
private
-
-
# @param items
-
# [String] plain html
-
# [Array<String, Array>] list of item names
-
# If an item is an array then the first element is used as header for a section.
-
# The second item has to be an array with the item names for that section.
-
# [Hash] key is used to identify active item, value is the item name.
-
# @param active specifies which item to highlight as active. If items are given as array
-
# it has to be the index of the active item. If items are given as hash it has to be
-
# the key of that item.
-
1
def dropdown_list items, extra_css_class=nil, active=nil
-
202
wrap_with :ul, class: "dropdown-menu #{extra_css_class}", role: "menu" do
-
list =
-
202
case items
-
when Array
-
202
dropdown_array_list items, active
-
when Hash
-
dropdown_hash_list items, active
-
else
-
[items]
-
end
-
202
list.flatten.compact.join "\n"
-
end
-
end
-
-
1
def dropdown_header text
-
190
content_tag(:h6, text, class: "dropdown-header")
-
end
-
-
1
def dropdown_hash_list items, active=nil
-
items.map { |key, item| dropdown_list_item item, key, active }
-
end
-
-
1
def dropdown_array_list items, active=nil
-
1422
items.map.with_index { |item, i| dropdown_list_item item, i, active }
-
end
-
-
1
def dropdown_list_item item, active_test, active
-
1030
return unless item
-
-
1018
if item.is_a? Array
-
190
[dropdown_header(item.first), dropdown_array_list(item.second)]
-
else
-
828
"<li class='dropdown-item#{' active' if active_test == active}'>#{item}</li>"
-
end
-
end
-
-
1
def split_button_toggle
-
202
wrap_with(:a,
-
href: "#",
-
class: "nav-link pl-0 dropdown-toggle dropdown-toggle-split",
-
"data-toggle" => "dropdown",
-
"aria-haspopup" => "true",
-
"aria-expanded" => "false") do
-
202
'<span class="sr-only">Toggle Dropdown</span>'
-
end
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/dropdown.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module Bootstrap;
-
# Set: All cards (Bootstrap, Form)
-
#
-
1
module Form;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/form.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def button_tag content_or_options=nil, options={}, &block
-
172
bootstrapify_button(block_given? ? content_or_options : options)
-
172
super(content_or_options, options, &block)
-
end
-
-
1
def bootstrapify_button options
-
172
situation = options.delete(:situation) || "primary"
-
172
options[:class] = [options[:class], "btn", "btn-#{situation}"].compact * " "
-
end
-
-
1
def type_field args={}
-
4
args[:class] ||= ""
-
4
args[:class] += " form-control"
-
4
super(args)
-
end
-
-
1
def bootstrap_options options
-
483
options[:class] ||= ""
-
483
options[:class] += " form-control"
-
483
options
-
end
-
-
1
FIELD_HELPERS = %w[hidden_field color_field date_field datetime_field
-
datetime_local_field email_field month_field number_field
-
password_field phone_field range_field search_field
-
telephone_field text_area text_field time_field
-
url_field week_field file_field].freeze
-
-
1
FIELD_HELPERS.each do |method_name|
-
19
define_method(method_name) do |name, options={}|
-
483
form.send(method_name, name, bootstrap_options(options))
-
end
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/form.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module Bootstrap;
-
# Set: All cards (Bootstrap, Helper)
-
#
-
1
module Helper;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/helper.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def button_link link_text, opts={}
-
btn_type = opts.delete(:btn_type) || "primary"
-
opts[:class] = [opts[:class], "btn btn-#{btn_type}"].compact.join " "
-
smart_link_to link_text, opts
-
end
-
-
1
def separator
-
'<li role="separator" class="divider"></li>'
-
end
-
-
1
def list_group content_or_options=nil, options={}
-
options = content_or_options if block_given?
-
content = block_given? ? yield : content_or_options
-
content = Array(content).map(&:to_s)
-
add_class options, "list-group"
-
options[:items] ||= {}
-
add_class options[:items], "list-group-item"
-
list_tag content, options
-
end
-
-
1
def list_tag content_or_options=nil, options={}
-
options = content_or_options if block_given?
-
content = block_given? ? yield : content_or_options
-
content = Array(content)
-
default_item_options = options.delete(:items) || {}
-
tag = options[:ordered] ? :ol : :ul
-
wrap_with tag, options do
-
content.map do |item|
-
i_content, i_opts = item
-
i_opts ||= default_item_options
-
wrap_with :li, i_content, i_opts
-
end
-
end
-
end
-
-
1
def badge_tag content, options={}
-
add_class options, "badge"
-
wrap_with :span, content, options
-
end
-
-
1
def popover_link text, title=nil, link_text=fa_icon("question-circle"), opts={}
-
link_to link_text, popover_opts(text, title, opts)
-
end
-
-
1
def popover_opts text, title, opts
-
255
add_class opts, "pl-1 text-muted-link"
-
255
opts.merge! path: "#", "data-toggle": "popover",
-
"data-trigger": :focus, "data-content": text
-
255
opts["data-title"] = title if title
-
255
opts
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/helper.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module Bootstrap;
-
# Set: All cards (Bootstrap, Icon)
-
#
-
1
module Icon;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/icon.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
# same for all:
-
# :search,
-
ICON_MAP = {
-
1
material: {
-
plus: :add,
-
pencil: :edit,
-
trash: :delete,
-
wrench: :build,
-
new_window: :open_in_new,
-
history: :history,
-
triangle_left: :expand_less,
-
triangle_right: :expand_more,
-
flag: :flag,
-
option_horizontal: :more_horiz,
-
option_vertical: :more_vert,
-
pushpin: :pin_drop,
-
baby_formula: :device_hub,
-
log_out: :call_made,
-
log_in: :call_received,
-
explore: :explore,
-
remove: :close,
-
expand: :expand_more,
-
collapse_down: :expand_less,
-
globe: :public,
-
check_circle_o: nil,
-
commenting: :comment
-
},
-
font_awesome: {
-
option_horizontal: :ellipsis_h,
-
pushpin: "thumb-tack",
-
globe: :globe,
-
zoom_out: "search-minus",
-
close: :remove,
-
check_circle_o: "check-circle-o",
-
check_circe: "check-circle",
-
reorder: "align-justify",
-
commenting: :commenting
-
},
-
glyphicon: {
-
option_horizontal: "option-horizontal",
-
option_vertical: "option-vertical",
-
triangle_left: "triangle-left",
-
triangle_right: "triangle-right",
-
baby_formula: "baby-formula",
-
log_out: "log-out",
-
log_in: "log-in",
-
collapse_down: "collapse-down",
-
globe: :globe,
-
zoom_out: "zoom-out",
-
close: :remove,
-
new_window: "new-window",
-
history: :time,
-
check_circle_o: "ok-circle",
-
check_circle: "ok-sign",
-
reorder: "align-justify"
-
}
-
-
}.freeze
-
-
1
def icon_class library, icon
-
1054
ICON_MAP[library][icon] || icon
-
end
-
-
1
def material_icon icon, opts={}
-
817
universal_icon_tag icon, :material, opts
-
end
-
-
1
def glyphicon icon, opts={}
-
universal_icon_tag icon, :glyphicon, opts
-
end
-
-
1
def fa_icon icon, opts={}
-
25
universal_icon_tag icon, :font_awesome, opts
-
end
-
-
1
def icon_tag icon, opts={}
-
212
opts = { class: opts } unless opts.is_a? Hash
-
212
library = opts.delete(:library) || default_icon_library
-
212
universal_icon_tag icon, library, opts
-
end
-
-
1
def universal_icon_tag icon, icon_library=default_icon_library, opts={}
-
1054
return "" unless icon.present?
-
-
1054
opts = { class: opts } unless opts.is_a? Hash
-
1054
icon_method = "#{icon_library}_icon_tag"
-
1054
send icon_method, icon, opts
-
end
-
-
1
def default_icon_library
-
212
:material
-
end
-
-
1
def glyphicon_icon_tag icon, opts={}
-
prepend_class opts, "glyphicon glyphicon-#{icon_class(:glyphicon, icon)}"
-
wrap_with :span, "", opts.merge("aria-hidden": true)
-
end
-
-
1
def font_awesome_icon_tag icon, opts={}
-
25
prepend_class opts, "fa fa-#{icon_class(:font_awesome, icon)}"
-
25
wrap_with :i, "", opts
-
end
-
-
1
def material_icon_tag icon, opts={}
-
1029
add_class opts, "material-icons"
-
1029
wrap_with :i, icon_class(:material, icon), opts
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/icon.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module Bootstrap;
-
# Set: All cards (Bootstrap, Navbar)
-
#
-
1
module Navbar;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/navbar.rb"; end
-
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
# Options
-
# @param opts [Hash]
-
# @option opts [String, Hash<name, href>] brand
-
# @option opts [String] class
-
# @option opts [Boolean] no_collapse
-
# @option opts [:left, :right] toggle_align
-
1
def navbar id, opts={}
-
nav_opts = opts[:navbar_opts] || {}
-
nav_opts[:class] ||= opts[:class]
-
add_class nav_opts,
-
"navbar navbar-dark bg-#{opts.delete(:navbar_type) || 'primary'}"
-
content = yield
-
if opts[:no_collapse]
-
navbar_nocollapse content, nav_opts
-
else
-
navbar_responsive id, content, opts, nav_opts
-
end
-
end
-
-
1
def navbar_nocollapse content, nav_opts
-
# content = wrap_with(:div, content)
-
wrap_with :nav, content, nav_opts
-
end
-
-
1
def navbar_responsive id, content, opts, nav_opts
-
opts[:toggle_align] ||= :right
-
wrap_with :nav, nav_opts do
-
[
-
navbar_header(opts[:brand]),
-
navbar_toggle(id, opts[:toggle_align]),
-
wrap_with(:div, class: "collapse navbar-collapse",
-
id: "navbar-collapse-#{id}") { content }
-
]
-
end
-
end
-
-
1
def navbar_header brand
-
return "" unless brand
-
if brand.is_a? String
-
"<span class='navbar-brand'>#{brand}</span>"
-
else
-
link = brand[:href] || "#"
-
"<a class='navbar-brand' href='#{link}#'>#{brand[:name]}</a>"
-
end
-
end
-
-
1
def navbar_toggle id, align
-
content ||= %(<span class="navbar-toggler-icon"></span>)
-
<<-HTML
-
<button class="navbar-toggler navbar-toggler-#{align}" type="button" data-toggle="collapse" data-target="#navbar-collapse-#{id}" aria-controls="navbar-collapse-#{id}" aria-expanded="false" aria-label="Toggle navigation">
-
#{content}
-
</button>
-
HTML
-
end
-
-
1
def breadcrumb items
-
wrap_with :ol, class: "breadcrumb" do
-
items.map do |item|
-
wrap_with :li, item, class: "breadcrumb-item"
-
end.join
-
end
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/navbar.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module Bootstrap;
-
# Set: All cards (Bootstrap, Table)
-
#
-
1
module Table;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/table.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
class TableHelper
-
1
def initialize format, content, opts={}
-
@format = format
-
@div_table = opts.delete :div_table
-
@header = initialize_header opts[:header], content
-
@rows = content
-
@opts = opts
-
@format.add_class opts, :table
-
end
-
-
1
def render
-
tag :table, class: @opts[:class] do
-
[header, body]
-
end
-
end
-
-
1
def header
-
return unless @header
-
tag :thead do
-
tag :tr do
-
@header.map do |item|
-
tag(:th) { item }
-
end.join "\n"
-
end
-
end
-
end
-
-
1
def body
-
tag :tbody do
-
@rows.map do |row_content|
-
row row_content
-
end.join "\n"
-
end
-
end
-
-
1
def row row
-
row_data, row_class =
-
case row
-
when Hash then
-
[row.delete(:content), row]
-
else
-
[row, {}]
-
end
-
row_content =
-
if row_data.is_a?(Array)
-
row_data.map { |item| cell item }.join "\n"
-
else
-
row_data
-
end
-
tag :tr, row_content, row_class
-
end
-
-
1
def cell cell
-
if cell.is_a? Hash
-
content = cell.delete(:content).to_s
-
tag :td, cell do
-
content
-
end
-
else
-
tag :td do
-
String(cell)
-
end
-
end
-
end
-
-
1
def tag elem, content_or_opts={}, opts={}, &block
-
if @div_table
-
if content_or_opts.is_a? Hash
-
@format.add_class content_or_opts, elem
-
else
-
@format.add_class opts, elem
-
end
-
elem = :div
-
end
-
@format.wrap_with elem, content_or_opts, opts, &block
-
end
-
-
1
private
-
-
1
def initialize_header header, content
-
case header
-
when Array then header
-
when nil then nil
-
else content.shift
-
end
-
end
-
end
-
-
# @param [Array<Array,String>] content the content for the table. Accepts
-
# strings or arrays for each row.
-
# @param [Hash] opts
-
# @option opts [String, Array] :header use first row of content as header or
-
# value of this option if it is a string
-
# @return [HTML] bootstrap table
-
1
def table content, opts={}
-
TableHelper.new(self, content, opts).render
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/table.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module Bootstrap;
-
# Set: All cards (Bootstrap, Tabs)
-
#
-
1
module Tabs;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/tabs.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
# @param tab_hash [Hash] keys are the tab names
-
# Each value can be either a String or a Hash.
-
# If a Hash can contain the following keys:
-
# :title - the label to appear in the clickable tab nav.
-
# if title is not specified, the key is used
-
# :content - body of tab pane
-
# :button_attr - attributes for button link in tab nav.
-
#
-
# If using lazy loading (see :load below), the following options also apply
-
# :path - explicit path to use for tab pane
-
# :view - card view from which to auto-construct path (if missing, uses key)
-
#
-
# If the value is a String, it is treated as the tab content for static tabs and
-
# the view for lazy tabs
-
#
-
# @param active_name [String] label of the tab that should be active at the
-
#
-
# @param [Hash] args options
-
# @option args [String] :tab_type ('tabs') use pills or tabs
-
# @option args [Hash] :panel_attr html args used for the panel div
-
# @option args [Hash] :pane_attr html args used for the pane div
-
# @option args [Hash] :load. `:lazy` for lazy-loading tabs
-
#
-
# @param [Block] block content of the active tab (for lazy-loading)
-
# beginning (default is the first)
-
#
-
# @return [HTML] bootstrap tabs element with all content preloaded
-
1
def tabs tab_hash, active_name=nil, args={}, &block
-
2
klass = args[:load] == :lazy ? Card::LazyTab : Card::Tab
-
2
args.reverse_merge!(
-
panel_attr: {},
-
pane_attr: {},
-
tab_type: "tabs",
-
block: block,
-
tab_objects: Card::Tab.tab_objects(self, tab_hash, active_name, klass)
-
)
-
2
haml :tab_panel, args
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/tabs.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module Bootstrap;
-
# Set: All cards (Bootstrap, Wrapper)
-
#
-
1
module Wrapper;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/wrapper.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def frame
-
52
class_up "d0-card-header" , "card-header"
-
52
class_up "d0-card-body", "card-body card-text"
-
52
super
-
end
-
-
1
def standard_frame slot=true
-
52
if panel_state
-
class_up "d0-card-frame", "card bg-#{panel_state} text-white"
-
else
-
52
class_up "d0-card-frame", "card"
-
end
-
52
super
-
end
-
-
1
def panel_state
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/wrapper.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (RichBootstrap)
-
#
-
1
module RichBootstrap;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/rich_bootstrap.rb"; end
-
1
def read_bootstrap_variables
-
path = ::File.expand_path(
-
"#{mod_root :bootstrap}/vendor/bootstrap/scss/_variables.scss", __FILE__
-
)
-
::File.exist?(path) ? ::File.read(path) : ""
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :closed do
-
class_up "d0-card-body", "closed-content"
-
super()
-
end
-
-
1
include Bootstrapper
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/rich_bootstrap.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "BootstrapCore"
-
#
-
1
module BootstrapCore;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/bootstrap_core.rb"; end
-
1
include_set Abstract::BootstrapCodeFile
-
-
1
def load_stylesheets
-
add_bs_stylesheet "variables"
-
add_bs_subdir "mixins"
-
%w[ print reboot type images code grid tables forms buttons transitions dropdown
-
button-group input-group custom-forms nav navbar card breadcrumb pagination badge
-
jumbotron alert progress media list-group close modal tooltip popover carousel
-
].each do |name|
-
add_bs_stylesheet name
-
end
-
add_bs_subdir "utilities"
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/bootstrap_core.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "BootstrapFunctions"
-
#
-
1
module BootstrapFunctions;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/bootstrap_functions.rb"; end
-
1
include_set Abstract::BootstrapCodeFile
-
-
1
def load_stylesheets
-
add_bs_stylesheet "functions"
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/bootstrap_functions.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "FontAwesome"
-
#
-
1
module FontAwesome;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/font_awesome.rb"; end
-
1
include_set Abstract::CodeFile
-
1
Self::StyleLibraries.add_item :font_awesome
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/font_awesome.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "MaterialIcons"
-
#
-
1
module MaterialIcons;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/material_icons.rb"; end
-
1
include_set Abstract::CodeFile
-
1
Self::StyleLibraries.add_item :material_icons
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/material_icons.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "ScriptBootstrap"
-
#
-
1
module ScriptBootstrap;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/script_bootstrap.rb"; end
-
1
include_set Abstract::CodeFile
-
1
Self::ScriptLibraries.add_item :script_bootstrap
-
-
1
def source_dir
-
""
-
end
-
-
1
def source_files
-
%w[vendor/bootstrap/dist/js/bootstrap.bundle.js
-
lib/javascript/bootstrap_modal_decko.js
-
vendor/bootstrap-colorpicker/dist/js/bootstrap-colorpicker.min.js]
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/script_bootstrap.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "ScriptLoadSelect2"
-
#
-
1
module ScriptLoadSelect2;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/script_load_select2.rb"; end
-
1
include_set Abstract::CodeFile
-
1
Self::ScriptMods.add_item :script_load_select2
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/script_load_select2.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "ScriptSelect2"
-
#
-
1
module ScriptSelect2;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/script_select2.rb"; end
-
1
include_set Abstract::CodeFile
-
1
Self::ScriptLibraries.add_item :script_select2
-
-
1
def source_files
-
# full version of select2 is needed to support containerCssClass config option
-
# which we need for select2_bootstrap to work properly
-
"vendor/select2/dist/js/select2.full.min.js"
-
end
-
-
1
def source_dir
-
""
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/script_select2.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "SmartmenuCss"
-
#
-
1
module SmartmenuCss;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/smartmenu_css.rb"; end
-
1
include_set Abstract::CodeFile
-
-
1
def source_files
-
"startmenu.css"
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/smartmenu_css.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "SmartmenuJs"
-
#
-
1
module SmartmenuJs;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/smartmenu_js.rb"; end
-
1
include_set Abstract::CodeFile
-
-
1
def source_files
-
"startmenu.js"
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/smartmenu_js.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "StyleBootstrapCards"
-
#
-
1
module StyleBootstrapCards;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/style_bootstrap_cards.rb"; end
-
1
include_set Abstract::CodeFile
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/style_bootstrap_cards.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "StyleBootstrapColorpicker"
-
#
-
1
module StyleBootstrapColorpicker;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/style_bootstrap_colorpicker.rb"; end
-
1
include_set Abstract::BootstrapCodeFile
-
1
Self::StyleLibraries.add_item :style_bootstrap_colorpicker
-
-
1
def load_stylesheets
-
add_stylesheet_file "vendor/bootstrap-colorpicker/src/sass/_colorpicker.scss"
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/style_bootstrap_colorpicker.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "StyleSelect2"
-
#
-
1
module StyleSelect2;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/style_select2.rb"; end
-
1
include_set Abstract::CodeFile
-
1
Self::StyleLibraries.add_item :style_select2
-
-
1
def source_files
-
["vendor/select2/dist/css/select2.css", "lib/stylesheets/style_select2_bootstrap.scss"]
-
end
-
-
1
def source_dir
-
""
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/style_select2.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "StyleSelect2Bootstrap"
-
#
-
1
module StyleSelect2Bootstrap;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/style_select2_bootstrap.rb"; end
-
1
include_set Abstract::CodeFile
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/style_select2_bootstrap.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "BootswatchSkin" cards
-
#
-
1
module BootswatchSkin;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/type/bootswatch_skin.rb"; end
-
1
include_set Abstract::BootswatchTheme
-
-
# override to customize the theme or to make it customizable
-
# @return [Card, String, Array<Card, String>] strings must be valid (s)css; cards
-
# must be of type (S)CSS
-
1
def variables_content
-
scss_from_theme_file :variables
-
end
-
-
# override to customize the theme or to make it customizable
-
# @return [Card, String, Array<Card,String>] strings must be valid (s)css; cards
-
# must be of type (S)CSS
-
1
def stylesheets_content
-
scss_from_theme_file :bootswatch
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/type/bootswatch_skin.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "CustomizedBootswatchSkin" cards
-
#
-
1
module CustomizedBootswatchSkin;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/type/customized_bootswatch_skin.rb"; end
-
1
include_set Abstract::BootswatchTheme
-
-
1
card_accessor :colors
-
1
card_accessor :variables
-
1
card_accessor :stylesheets
-
-
1
def top_level_item_cards
-
cards = PRE_VARIABLES_CARD_NAMES.map { |n| Card[n] }
-
cards += [colors_card, variables_card]
-
cards += POST_VARIABLES_CARD_NAMES.map { |n| Card[n] }
-
cards << stylesheets_card
-
cards
-
end
-
-
1
def editable_item_cards
-
[colors_card, variables_card, stylesheets_card]
-
end
-
-
1
def variables_card_names
-
%i[colors variables].map { |s| Card.fetch_name name, s }
-
end
-
-
1
def stylesheets_card_names
-
[Card.fetch_name(name, :stylesheets)]
-
end
-
-
1
def theme_card_name
-
"#{theme_name} skin"
-
end
-
-
1
def theme_name
-
Env.params[:theme].present? && Env.params[:theme]
-
end
-
-
1
def theme_codename
-
theme_name && "#{theme_name}_skin".to_sym
-
end
-
-
1
event :validate_theme_template, :validate, on: :create do
-
if theme_name
-
if Card.fetch_type_id(theme_card_name) != Card::BootswatchSkinID
-
errors.add :abort, tr(:not_valid_theme, theme_name: theme_name)
-
elsif !Dir.exist? source_dir
-
puts method(:source_dir).source_location
-
errors.add :abort, tr(:cannot_source_theme, theme_name: theme_name)
-
end
-
end
-
end
-
-
1
event :initialize_because_of_type_change, :prepare_to_store,
-
on: :update, changed: :type do
-
initialize_theme old_skin_items
-
end
-
-
1
def old_skin_items
-
skin = Card.new(type: :pointer, content: db_content_before_act)
-
skin.drop_item "bootstrap default skin"
-
skin.item_names
-
end
-
-
1
event :copy_theme, :prepare_to_store, on: :create do
-
initialize_theme
-
end
-
-
1
def initialize_theme style_item_names=nil
-
add_subfield :colors, type_id: Card::ScssID
-
add_variables_subfield
-
add_stylesheets_subfield style_item_names
-
end
-
-
1
def add_stylesheets_subfield style_items=nil
-
opts = { type_id: Card::SkinID }
-
if theme_name
-
theme_style = add_bootswatch_subfield
-
opts[:content] = "[[#{theme_style.name}]]"
-
end
-
if style_items
-
opts[:content] = [opts[:content], style_items].flatten.compact.to_pointer_content
-
end
-
-
add_subfield :stylesheets, opts
-
end
-
-
1
def add_variables_subfield
-
theme_content = content_from_theme(:variables)
-
default_content = read_bootstrap_variables
-
add_subfield :variables,
-
type_id: Card::ScssID,
-
content: "#{theme_content}\n\n\n#{default_content}"
-
end
-
-
1
def add_bootswatch_subfield
-
add_subfield :bootswatch, type_id: Card::ScssID,
-
content: content_from_theme(:bootswatch)
-
end
-
-
1
def theme_card
-
@theme_card ||= theme_codename ? Card[theme_codename] : nil
-
end
-
-
1
def content_from_theme subfield
-
theme_card&.scss_from_theme_file subfield
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def edit_fields
-
[[:colors, { title: "" }],
-
[:variables, { title: "Variables" }],
-
[:stylesheets, { title: "Styles" }]]
-
end
-
-
1
view :one_line_content do
-
""
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/type/customized_bootswatch_skin.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Type; module CustomizedBootswatchSkin;
-
# Set: All "CustomizedBootswatchSkin+HtmlViews" cards (HtmlViews)
-
#
-
1
module HtmlViews;
-
1
extend Card::Set
-
2
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/type/customized_bootswatch_skin/html_views.rb"; end
-
1
include_set Abstract::Media
-
1
include_set Abstract::BsBadge
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :menu do
-
""
-
end
-
-
1
def short_content
-
""
-
# labeled_badge card.item_count, "items"
-
# "#{card.item_count} items"
-
end
-
-
1
view :core, template: :haml
-
-
1
info_bar_cols 6, 3, 3
-
-
1
before :bar do
-
super()
-
voo.show :edit_button, :bar_middle
-
class_up "bar-middle", "p-3 align-items-center p-0"
-
end
-
-
1
view :bar_right do
-
render(:short_content)
-
end
-
-
1
before :bar_nav do
-
voo.hide :edit_link
-
end
-
-
1
view :bar_bottom do
-
render_core
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/type/customized_bootswatch_skin/html_views.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class TypePlusRight; module CustomizedBootswatchSkin;
-
# Set: All "+Colors" cards on "CustomizedBootswatchSkin" cards
-
#
-
1
module Colors;
-
1
extend Card::Set
-
3
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/type_plus_right/customized_bootswatch_skin/colors.rb"; end
-
VARIABLE_NAMES = {
-
1
colors: %i[blue indigo purple pink red orange yellow green teal cyan
-
white gray-100 gray-200 gray-300 gray-400 gray-500 gray-600 gray-700 gray-800
-
gray-900 black],
-
theme_colors: %i[primary secondary success info warning danger light dark
-
body-bg body-color]
-
}.freeze
-
-
# temporarily removed: link-color card-bg card-cap-bg
-
# bootstrap default for link-color uses the theme-color function which
-
# has to be defined between the theme-colors and that variable
-
# (see bootstrap's _variables.scss)
-
# TODO: deal with that
-
-
# @param name [String] a scss variable name (it can start with a $)
-
1
def variable_value name
-
value_from_scss(name, content) ||
-
value_from_variables_card(name) ||
-
default_value_from_bootstrap(name)
-
end
-
-
1
def value_from_scss name, source
-
name = name.to_s
-
name = name[1..-1] if name.start_with?("$")
-
source.match(definition_regex(name))&.capture(:value)
-
end
-
-
1
def value_from_variables_card name
-
return unless (var_card = left.variables_card) && var_card.content.present?
-
value_from_scss name, var_card.content
-
end
-
-
1
def definition_regex name
-
/^(?<before>\s*\$#{name}\:\s*)(?<value>.+?)(?<after> !default;)$/
-
end
-
-
1
def default_value_from_bootstrap name
-
value_from_scss name, bootstrap_variables_scss
-
end
-
-
1
def bootstrap_variables_scss
-
@bootstrap_variables_scss ||= read_bootstrap_variables
-
end
-
-
1
def colors
-
@colors ||= variable_group_with_values :colors
-
end
-
-
1
def theme_colors
-
@theme_colors ||= variable_group_with_values :theme_colors
-
end
-
-
1
def variable_group_with_values group
-
VARIABLE_NAMES[group].each_with_object({}) do |name, h|
-
h[name] = variable_value name
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :input, template: :haml do
-
@colors = card.colors
-
@theme_colors = card.theme_colors
-
end
-
-
1
def theme_color_picker name, value
-
# value = value[1..-1] if value.start_with? "$"
-
options = VARIABLE_NAMES[:colors].map { |var| "$#{var}" }
-
options << value unless options.include? value
-
select_tag "theme_colors[#{name}]", options_for_select(options, value),
-
class: "tags form-control"
-
end
-
-
1
before :bar_right do
-
voo.show :edit_button
-
end
-
-
1
view :core, template: :haml do
-
@colors = card.theme_colors.reject { |k, _v| k.in? %i[body-bg body-color] }
-
end
-
-
1
view :bar_middle do
-
<<-HTML
-
<div class="colorpicker-element">
-
<div class="input-group-addon">
-
<span class="bg-body border p-1">Text</span>
-
<span class="bg-dark text-light border p-1">Nav</span>
-
<i class="bg-primary"></i>
-
<i class="bg-secondary"></i>
-
</div>
-
</div>
-
HTML
-
end
-
end
-
-
1
event :translate_variables_to_scss, :prepare_to_validate, on: :update do
-
replace_values :colors
-
replace_values :theme_colors
-
end
-
-
1
def replace_values group, prefix=""
-
values = variable_values_from_params group
-
values.each_pair do |name, val|
-
if content.match definition_regex(name)
-
content.gsub! definition_regex(name), "\\k<before>#{prefix}#{val}\\k<after>"
-
else
-
self.content += "$#{name}: #{prefix}#{val} !default;\n"
-
end
-
end
-
end
-
-
1
def variable_values_from_params group
-
Env.params.dig(group)&.slice(*VARIABLE_NAMES[group]) || {}
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/type_plus_right/customized_bootswatch_skin/colors.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (History)
-
#
-
1
module History;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-history/set/all/history.rb"; end
-
1
event :update_ancestor_timestamps, :integrate do
-
216
ids = history_ancestor_ids
-
216
return unless ids.present?
-
29
Card.where(id: ids).update_all(updater_id: Auth.current_id, updated_at: Time.now)
-
58
ids.map { |anc_id| Card.expire anc_id.cardname }
-
end
-
-
# track history (acts, actions, changes) on this card
-
1
def history?
-
659
true
-
end
-
-
# all cards whose acts are considered part of this card's history
-
1
def history_card_ids
-
nestee_ids << id
-
end
-
-
# all cards who are considered updated if this card's was updated
-
1
def history_parent_ids
-
490
nester_ids
-
end
-
-
1
def history_ancestor_ids recursion_level=0
-
245
return [] if recursion_level > 5
-
-
245
ids = history_parent_ids +
-
29
history_parent_ids.map { |id| Card[id].history_ancestor_ids(recursion_level + 1) }
-
245
ids.flatten
-
end
-
-
# ~~FIXME~~: optimize (no need to instantiate all actions and changes!)
-
# Nothing is instantiated here. ActiveRecord is much smarter than you think.
-
# Methods like #empty? and #size make sql queries if their receivers are not already
-
# loaded -pk
-
1
def first_change?
-
# = update or delete
-
158
@current_action.action_type != :create && action_count == 2 &&
-
create_action.card_changes.empty?
-
end
-
-
1
def first_create?
-
158
@current_action.action_type == :create && action_count == 1
-
end
-
-
1
def action_count
-
158
Card::Action.where(card_id: @current_action.card_id).count
-
end
-
-
# card has account that is responsible for prior acts
-
1
def has_edits?
-
Card::Act.where(actor_id: id).where("card_id IS NOT NULL").present?
-
end
-
-
1
def changed_fields
-
211
Card::Change::TRACKED_FIELDS & (changed_attribute_names_to_save | saved_changes.keys)
-
end
-
-
1
def nestee_ids
-
78
requiring_id { @nestee_ids ||= nesting_ids(:referee_id, :referer_id) }
-
end
-
-
1
def nester_ids
-
980
requiring_id { @nester_ids ||= nesting_ids(:referer_id, :referee_id) }
-
end
-
-
1
def diff_args
-
2
{ diff_format: :text }
-
end
-
-
# Delete all changes and old actions and make the last action the create action
-
# (that way the changes for that action will be created with the first update)
-
1
def make_last_action_the_initial_action
-
delete_all_changes
-
old_actions.delete_all
-
last_action.update! action_type: :create
-
end
-
-
1
def clear_history
-
delete_all_changes
-
delete_old_actions
-
end
-
-
1
def delete_old_actions
-
old_actions.delete_all
-
end
-
-
1
def delete_all_changes
-
Card::Change.where(card_action_id: all_action_ids).delete_all
-
end
-
-
1
def save_content_draft content
-
super
-
acts.create do |act|
-
act.ar_actions.build(draft: true, card_id: id, action_type: :update)
-
.card_changes.build(field: :db_content, value: content)
-
end
-
end
-
-
1
private
-
-
1
def nesting_ids return_field, where_field
-
236
Card::Reference.select(return_field).distinct.where(
-
ref_type: "I", where_field => id
-
).pluck(return_field).compact
-
end
-
-
1
def requiring_id
-
529
id ? yield : (return [])
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-history/set/all/history.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module History;
-
# Set: All cards (History, ActListing)
-
#
-
1
module ActListing;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-history/set/all/history/act_listing.rb"; end
-
1
ACTS_PER_PAGE = Card.config.acts_per_page
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def act_from_context
-
2
if (act_id = params["act_id"])
-
Act.find(act_id) || raise(Card::NotFound, "act not found")
-
else
-
2
card.last_action.act
-
end
-
end
-
-
# used (by history and recent)for rendering act lists with legend and paging
-
#
-
# @param acts [ActiveRecord::Relation] relation that will return acts objects
-
# @param context [Symbol] :relative or :absolute
-
# @param draft_legend [Symbol] :show or :hide
-
1
def acts_layout acts, context, draft_legend=:hide
-
bs_layout container: false, fluid: false do
-
html _render_act_legend(draft_legend => :draft_legend)
-
row(12) { act_list acts, context }
-
row(12) { act_paging acts, context }
-
end
-
end
-
-
1
def act_list acts, context
-
act_accordion acts, context do |act, seq|
-
fmt = context == :relative ? self : act.card.format(:html)
-
fmt.act_listing act, seq, context
-
end
-
end
-
-
1
def act_listing act, seq=nil, context=nil
-
2
opts = act_listing_opts_from_params(seq)
-
2
opts[:slot_class] = "revision-#{act.id} history-slot list-group-item"
-
2
context ||= (params[:act_context] || :absolute).to_sym
-
2
act_renderer(context).new(self, act, opts).render
-
end
-
-
# TODO: consider putting all these under one top-level param, eg:
-
# act: { seq: X, diff: [show/hide], action_view: Y }
-
1
def act_listing_opts_from_params seq
-
2
{ act_seq: (seq || params["act_seq"]),
-
2
action_view: (params["action_view"] || "summary").to_sym,
-
hide_diff: params["hide_diff"].to_s.strip == "true" }
-
end
-
-
1
def act_accordion acts, context, &block
-
accordion_group acts_for_accordion(acts, context, &block), nil, class: "clear-both"
-
end
-
-
1
def acts_for_accordion acts, context
-
clean_acts(current_page_acts(acts)).map do |act|
-
with_act_seq(context, acts) do |seq|
-
yield act, seq
-
end
-
end
-
end
-
-
1
def with_act_seq context, acts
-
yield(context == :absolute ? nil : current_act_seq(acts))
-
end
-
-
1
def current_act_seq acts
-
@act_seq = @act_seq ? (@act_seq -= 1) : act_list_starting_seq(acts)
-
end
-
-
1
def clean_acts acts
-
# FIXME: if we get rid of bad act data, this will not be necessary
-
# The current
-
acts.select(&:card)
-
end
-
-
1
def current_page_acts acts
-
acts.page(acts_page_from_params).per acts_per_page
-
end
-
-
1
def act_list_starting_seq acts
-
acts.size - (acts_page_from_params - 1) * acts_per_page
-
end
-
-
1
def acts_per_page
-
@acts_per_page || ACTS_PER_PAGE
-
end
-
-
1
def acts_page_from_params
-
@acts_page_from_params ||= params["page"].present? ? params["page"].to_i : 1
-
end
-
-
1
def act_paging acts, context
-
wrap_with :div, class: "slotter btn-sm" do
-
acts = current_page_acts acts
-
opts = { remote: true, theme: "twitter-bootstrap-4" }
-
opts[:total_pages] = 10 if limited_paging? context
-
paginate acts, opts
-
end
-
end
-
-
1
def limited_paging? context
-
context == :absolute && Act.count > 1000
-
end
-
-
1
def action_icon action_type, extra_class=nil
-
2
icon = case action_type
-
when :create then :add_circle
-
2
when :update then :pencil
-
when :delete then :remove_circle
-
when :draft then :wrench
-
end
-
2
icon_tag icon, extra_class
-
end
-
-
1
private
-
-
1
def act_renderer context
-
2
case context
-
when :absolute
-
2
Act::ActRenderer::AbsoluteActRenderer
-
when :bridge
-
Act::ActRenderer::BridgeActRenderer
-
else # relative
-
Act::ActRenderer::RelativeActRenderer
-
end
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-history/set/all/history/act_listing.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module History;
-
# Set: All cards (History, Actions)
-
#
-
# -*- encoding : utf-8 -*-
-
1
module Actions;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-history/set/all/history/actions.rb"; end
-
-
1
def all_action_ids
-
Card::Action.where(card_id: id).pluck :id
-
end
-
-
1
def action_from_id action_id
-
289
return unless action_id.is_a?(Integer) || action_id =~ /^\d+$/
-
-
# if not an integer revision id is probably a mod (e.g. if you request
-
# files/:logo/standard.png)
-
16
action = Action.fetch action_id
-
16
return unless action.card_id == id
-
-
16
action
-
end
-
-
1
def old_actions
-
actions.where("id != ?", last_action_id)
-
end
-
-
1
def create_action
-
111
@create_action ||= actions.first
-
end
-
-
1
def nth_action index
-
289
index = index.to_i
-
289
return unless id && index.positive?
-
-
Action.where("draft is not true AND card_id = #{id}")
-
.order(:id).limit(1).offset(index - 1).first
-
end
-
-
1
def new_content_action_id
-
1157
return unless @current_action && current_action_changes_content?
-
-
156
@current_action.id
-
end
-
-
1
def current_action_changes_content?
-
176
new_card? || @current_action.new_content? || db_content_is_changing?
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def action_from_context
-
2
if (action_id = voo.action_id || params[:action_id])
-
2
Action.fetch action_id
-
else
-
card.last_action
-
end
-
end
-
-
1
def action_content action, view_type
-
2
return "" unless action.present?
-
-
2
wrap do
-
2
[action_content_toggle(action, view_type),
-
content_diff(action, view_type)]
-
end
-
end
-
-
1
def content_diff action, view_type
-
2
diff = action.new_content? && content_changes(action, view_type)
-
2
return "<i>empty</i>" unless diff.present?
-
-
2
diff
-
end
-
-
1
def action_content_toggle action, view_type
-
2
return unless show_action_content_toggle?(action, view_type)
-
-
toggle_action_content_link action, view_type
-
end
-
-
1
def show_action_content_toggle? action, view_type
-
2
view_type == :expanded || action.summary_diff_omits_content?
-
end
-
-
1
def toggle_action_content_link action, view_type
-
other_view_type = view_type == :expanded ? :summary : :expanded
-
css_class = "revision-#{action.card_act_id} float-right"
-
link_to_view "action_#{other_view_type}",
-
icon_tag(action_arrow_dir(view_type), class: "md-24"),
-
class: css_class,
-
path: { action_id: action.id, look_in_trash: true }
-
end
-
-
1
def action_arrow_dir view_type
-
view_type == :expanded ? :triangle_left : :triangle_right
-
end
-
-
1
def revert_actions_link link_text, path_args, html_args={}
-
2
return unless card.ok? :update
-
-
2
path_args.reverse_merge! action: :update, look_in_trash: true, assign: true,
-
card: { skip: :validate_renaming }
-
2
html_args.reverse_merge! remote: true, method: :post, rel: "nofollow", path: path_args
-
2
add_class html_args, "slotter"
-
2
link_to link_text, html_args
-
end
-
-
1
def action_legend
-
types = %i[create update delete]
-
legend = types.map do |action_type|
-
"#{action_icon(action_type)} #{action_type}d"
-
end
-
legend << _render_draft_legend if voo.show?(:draft_legend)
-
"<small>Actions: #{legend.join ' | '}</small>"
-
end
-
-
1
def content_legend
-
legend = [Card::Content::Diff.render_added_chunk("Additions"),
-
Card::Content::Diff.render_deleted_chunk("Subtractions")]
-
"<small>Content changes: #{legend.join ' | '}</small>"
-
end
-
-
1
def content_changes action, diff_type, hide_diff=false
-
2
if hide_diff
-
action.raw_view
-
else
-
2
action.content_diff diff_type
-
end
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-history/set/all/history/actions.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module History;
-
# Set: All cards (History, Acts)
-
#
-
# all acts with actions on self and on cards included in self (ie, acts shown in history)
-
1
module Acts;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-history/set/all/history/acts.rb"; end
-
1
def history_acts
-
@history_acts ||= Act.all_with_actions_on(history_card_ids, true).order id: :desc
-
end
-
-
1
def draft_acts
-
drafts.created_by(Card::Auth.current_id).map(&:act)
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-history/set/all/history/acts.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module History;
-
# Set: All cards (History, Events)
-
#
-
# must be called on all actions and before :set_name, :process_subcards and
-
1
module Events;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-history/set/all/history/events.rb"; end
-
# :validate_delete_children
-
1
event :assign_action, :initialize, when: :actionable? do
-
362
act = director.need_act
-
362
@current_action = Card::Action.create(
-
card_act_id: act.id,
-
action_type: action,
-
362
draft: (Env.params["draft"] == "true")
-
)
-
362
if @supercard && @supercard != self
-
183
@current_action.super_action = @supercard.current_action
-
end
-
end
-
-
# can we store an action? (can be overridden, eg in files)
-
1
def actionable?
-
691
history?
-
end
-
-
1
event :detect_conflict, :validate, on: :update, when: :edit_conflict? do
-
2
errors.add :conflict, tr(:error_not_latest_revision)
-
end
-
-
1
def edit_conflict?
-
78
last_action_id_before_edit &&
-
last_action_id_before_edit.to_i != last_action_id &&
-
3
(la = last_action) &&
-
la.act.actor_id != Auth.current_id
-
end
-
-
# stores changes in the changes table and assigns them to the current action
-
# removes the action if there are no changes
-
1
event :finalize_action, :finalize, when: :finalize_action? do
-
174
if changed_fields.present?
-
158
@current_action.update! card_id: id
-
-
# Note: #last_change_on uses the id to sort by date
-
# so the changes for the create changes have to be created before the first change
-
158
store_card_changes_for_create_action if first_change?
-
158
store_card_changes unless first_create?
-
# FIXME: a `@current_action.card` call here breaks specs in solid_cache_spec.rb
-
16
elsif @current_action.card_changes.reload.empty?
-
16
@current_action.delete
-
16
@current_action = nil
-
end
-
end
-
-
# changes for the create action are stored after the first update
-
1
def store_card_changes_for_create_action
-
27
Card::Action.cache.delete "#{create_action.id}-changes"
-
27
store_each_history_field create_action.id do |field|
-
162
attribute_before_act field
-
end
-
end
-
-
1
def store_card_changes
-
37
store_each_history_field @current_action.id, changed_fields do |field|
-
49
self[field]
-
end
-
end
-
-
1
def store_each_history_field action_id, fields=nil
-
64
fields ||= Card::Change::TRACKED_FIELDS
-
if false # Card::Change.supports_import?
-
# attach.feature fails with this
-
values = fields.map.with_index { |field, index| [index, yield(field), action_id] }
-
Card::Change.import [:field, :value, :card_action_id], values #, validate: false
-
else
-
64
fields.each do |field|
-
211
Card::Change.create field: field,
-
value: yield(field),
-
card_action_id: action_id
-
end
-
end
-
end
-
-
1
def finalize_action?
-
216
actionable? && current_action
-
end
-
-
1
event :rollback_actions, :prepare_to_validate, on: :update, when: :rollback_request? do
-
update_args = process_revert_actions
-
Env.params["revert_actions"] = nil
-
update! update_args
-
clear_drafts
-
abort :success
-
end
-
-
1
event :finalize_act, after: :finalize_action, when: :act_card? do
-
88
Card::Director.act.update! card_id: id
-
end
-
-
1
event :remove_empty_act, :integrate_with_delay_final, when: :remove_empty_act? do
-
# Card::Director.act.delete
-
# Card::Director.act = nil
-
end
-
-
1
def remove_empty_act?
-
216
act_card? && Director.act&.ar_actions&.reload&.empty?
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-history/set/all/history/events.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module History;
-
# Set: All cards (History, Last)
-
#
-
1
module Last;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-history/set/all/history/last.rb"; end
-
1
def acted_at
-
last_act.acted_at
-
end
-
-
1
def revised_at
-
(last_action && (act = last_action.act) && act.acted_at) || Time.zone.now
-
end
-
-
1
def last_change_on field, opts={}
-
26
action_id = extract_action_id(opts[:before] || opts[:not_after])
-
-
# If there is only one action then there are no entries in the changes table,
-
# so we can't do a sql search but the changes are accessible via the action.
-
26
if no_last_change? action_id, opts[:before]
-
nil
-
26
elsif create_action_last_change? action_id
-
create_action&.change field
-
else
-
26
last_change_from_action_id action_id, field, opts
-
end
-
end
-
-
1
def no_last_change? action_id, before
-
26
before && action_id == create_action.id
-
end
-
-
1
def create_action_last_change? action_id
-
26
action_id == create_action&.id || (!action_id && create_action&.sole?)
-
end
-
-
1
def last_change_from_action_id action_id, field, opts
-
26
Change.joins(:action).where(
-
last_change_sql_conditions(opts),
-
card_id: id,
-
action_id: action_id,
-
field: Card::Change.field_index(field)
-
).order(:id).last
-
end
-
-
1
def last_change_sql_conditions opts
-
26
cond = "card_actions.card_id = :card_id AND field = :field"
-
26
cond += " AND (draft is not true)" unless opts[:including_drafts]
-
26
operator = "<" if opts[:before]
-
26
operator = "<=" if opts[:not_after]
-
26
cond += " AND card_action_id #{operator} :action_id" if operator
-
26
cond
-
end
-
-
1
def last_action_id
-
55
last_action&.id
-
end
-
-
1
def last_action
-
96
actions.where("id IS NOT NULL").last
-
end
-
-
1
def last_content_action
-
last_change_on(:db_content)&.action
-
end
-
-
1
def last_content_action_id
-
last_change_on(:db_content)&.card_action_id
-
end
-
-
1
def last_actor
-
last_act.actor
-
end
-
-
1
def last_act
-
@last_act ||=
-
if (action = last_action)
-
last_act_on_self = acts.last
-
act_of_last_action = action.act
-
return act_of_last_action unless last_act_on_self
-
return last_act_on_self unless act_of_last_action
-
-
return last_act_on_self if act_of_last_action == last_act_on_self
-
-
if last_act_on_self.acted_at > act_of_last_action.acted_at
-
last_act_on_self
-
else
-
act_of_last_action
-
end
-
end
-
end
-
-
1
def previous_action action_id
-
return unless action_id
-
-
action_index = actions.find_index { |a| a.id == action_id }
-
all_actions[action_index - 1] if action_index.to_i.nonzero?
-
end
-
-
1
private
-
-
1
def extract_action_id action_arg
-
26
action_arg.is_a?(Card::Action) ? action_arg.id : action_arg
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-history/set/all/history/last.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module History;
-
# Set: All cards (History, Revision)
-
#
-
1
module Revision;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-history/set/all/history/revision.rb"; end
-
1
def revision action, before_action=false
-
# a "revision" refers to the state of all tracked fields
-
# at the time of a given action
-
action = Card::Action.fetch(action) if action.is_a? Integer
-
return unless action
-
-
if before_action
-
revision_before_action action
-
else
-
revision_attributes action
-
end
-
end
-
-
1
def revision_attributes action
-
Card::Change::TRACKED_FIELDS.each_with_object({}) do |field, attr_changes|
-
last_change = action.change(field) || last_change_on(field, not_after: action)
-
attr_changes[field.to_sym] = (last_change ? last_change.value : self[field])
-
end
-
end
-
-
1
def revision_before_action action
-
if (prev_action = action.previous_action)
-
revision prev_action
-
else
-
{ trash: true }
-
end
-
end
-
-
1
def rollback_request?
-
153
history? && actions_to_revert.any?
-
end
-
-
1
def process_revert_actions revert_actions=nil
-
revert_actions ||= actions_to_revert
-
update_args = { subcards: {} }
-
reverting_to_previous = Env.params["revert_to"] == "previous"
-
revert_actions.each do |action|
-
merge_revert_action! action, update_args, reverting_to_previous
-
end
-
update_args
-
end
-
-
1
def actions_to_revert
-
115
if (act_id = Env.params["revert_act"])
-
Act.find(act_id).actions
-
else
-
115
explicit_actions_to_revert
-
end
-
end
-
-
1
def explicit_actions_to_revert
-
115
Array.wrap(Env.params["revert_actions"]).map do |a_id|
-
Action.fetch(a_id) || nil
-
end.compact
-
end
-
-
1
def merge_revert_action! action, update_args, reverting_to_previous
-
rev = action.card.revision(action, reverting_to_previous)
-
rev.delete :name unless rev[:name] # handles null name field in compound cards
-
if action.card_id == id
-
update_args.merge! rev
-
else
-
update_args[:subcards][action.card.name] = rev
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-history/set/all/history/revision.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module History;
-
# Set: All cards (History, Selected)
-
#
-
# if these aren't in a nested module, the methods just overwrite the base
-
# methods, but we need a distinct module so that super will be able to refer to
-
1
module Selected;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-history/set/all/history/selected.rb"; end
-
# the base methods.
-
1
def content
-
10015
@selected_action_id ? selected_content : super
-
end
-
-
1
def content= value
-
391
@selected_content = nil
-
391
super
-
end
-
-
1
def select_action_by_params params
-
289
action = nth_action(params[:rev]) || action_from_id(params[:rev_id])
-
289
return unless action
-
-
16
select_action action.id
-
end
-
-
1
def select_action action_id
-
16
run_callbacks :select_action do
-
16
self.selected_action_id = action_id
-
end
-
end
-
-
1
def selected_action_id
-
33
@selected_action_id || (@current_action&.id) || last_action_id
-
end
-
-
1
def selected_action_id= action_id
-
45
@selected_content = nil
-
45
@selected_action_id = action_id
-
end
-
-
1
def selected_action
-
12
selected_action_id && Action.fetch(selected_action_id)
-
end
-
-
1
def selected_content
-
131
@selected_content ||= content_at_time_of_selected_action || db_content
-
end
-
-
1
def content_at_time_of_selected_action
-
22
lc = last_change_on(:db_content, not_after: @selected_action_id, including_drafts: true)
-
22
lc&.value
-
end
-
-
1
def with_selected_action_id action_id
-
12
current_action_id = @selected_action_id
-
12
select_action_id action_id
-
12
result = yield
-
12
select_action_id current_action_id
-
12
result
-
end
-
-
1
def select_action_id action_id
-
24
run_callbacks :select_action do
-
24
self.selected_action_id = action_id
-
end
-
end
-
-
1
def selected_content_action_id
-
1389
@selected_action_id || new_content_action_id || last_content_action_id
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-history/set/all/history/selected.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module History;
-
# Set: All cards (History, Views)
-
#
-
# History views
-
1
module Views;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-history/set/all/history/views.rb"; end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :history, cache: :never do
-
frame do
-
class_up "d0-card-body", "history-slot"
-
acts_layout card.history_acts, :relative, :show
-
end
-
end
-
-
1
view :act, cache: :never do
-
2
act_listing act_from_context
-
end
-
-
1
view :act_legend do
-
bs_layout do
-
row md: [12, 12], lg: [7, 5] do
-
col action_legend
-
col content_legend, class: "text-right"
-
end
-
end
-
end
-
-
1
view :draft_legend do
-
"#{action_icon(:draft)} unsaved draft"
-
end
-
-
1
view :action_summary do
-
2
action_content action_from_context, :summary
-
end
-
-
1
view :action_expanded do
-
action_content action_from_context, :expanded
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-history/set/all/history/views.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (HistoryBridge)
-
#
-
1
module HistoryBridge;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-history/set/all/history_bridge.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :creator_credit,
-
wrap: { div: { class: "text-muted creator-credit" } }, cache: :never do
-
return "" unless card.real?
-
"Created by #{nest card.creator, view: :link} "\
-
"#{time_ago_in_words(card.created_at)} ago"
-
end
-
-
1
view :updated_by, wrap: { div: { class: "text-muted" } } do
-
return "" unless card.id
-
updaters = Card.search(updater_of: { id: card.id })
-
return "" unless updaters.present?
-
-
updaters = updater_links updaters, others_target: Card.fetch(card, :editors)
-
"Updated by #{updaters}"
-
end
-
-
1
def updater_links updaters, item_view: :link, max_count: 3, others_target: card
-
return "" unless updaters.present?
-
-
total = updaters.size
-
fetch_count = total > max_count ? max_count - 1 : max_count
-
-
reduced = first_card(fetch_count).map { |c| nest c, view: item_view }
-
if total > max_count
-
reduced << link_to_card(others_target, "#{total - fetch_count} others")
-
end
-
reduced.to_sentence
-
end
-
-
1
def acts_bridge_layout acts, context=:bridge
-
output [
-
_render_creator_credit,
-
act_link_list(acts, context),
-
act_paging(acts, context)
-
]
-
end
-
-
1
def act_link_list acts, context
-
items = acts_for_accordion(acts, context) do |act, seq|
-
act_link_list_item act, seq, context
-
end
-
bridge_pills items
-
end
-
-
1
def act_link_list_item act, seq=nil, _context=nil
-
opts = act_listing_opts_from_params(seq)
-
opts[:slot_class] = "revision-#{act.id} history-slot nav-item"
-
act_renderer(:bridge).new(self, act, opts).bridge_link
-
end
-
-
1
def act_list_group acts, context, &block
-
list_group acts_for_accordion(acts, context, &block), class: "clear-both"
-
end
-
-
1
view :bridge_act, cache: :never do
-
opts = act_listing_opts_from_params(nil)
-
act = act_from_context
-
ar = act_renderer(:bridge).new(self, act, opts)
-
class_up "action-list", "my-3"
-
wrap_with_overlay title: ar.overlay_title, slot: breadcrumb_data("History") do
-
act_listing(act, opts[:act_seq], :bridge)
-
end
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-history/set/all/history_bridge.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (Attachment)
-
#
-
1
module Attachment;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment.rb"; end
-
1
attr_writer :empty_ok
-
-
1
def self.included host_class
-
2
host_class.extend CarrierWave::CardMount
-
end
-
-
1
event :select_file_revision, after: :select_action do
-
45
attachment.retrieve_from_store!(attachment.identifier)
-
end
-
-
# we need a card id for the path so we have to update db_content when we have
-
# an id
-
6
event :correct_identifier, :finalize, on: :create, when: proc { |c| !c.web? } do
-
5
update_column(:db_content, attachment.db_content)
-
5
expire
-
end
-
-
1
event :save_original_filename, :prepare_to_store, on: :save, when: :file_ready_to_save? do
-
9
return unless @current_action
-
9
@current_action.update! comment: original_filename
-
end
-
-
1
event :validate_file_exist, :validate, on: :create do
-
6
return if empty_ok?
-
6
if will_be_stored_as == :web
-
errors.add "url is missing" if content.blank?
-
6
elsif !attachment.file.present?
-
1
errors.add attachment_name, "is missing"
-
end
-
end
-
-
10
event :write_identifier, after: :save_original_filename, when: proc { |c| !c.web? } do
-
26
self.content = attachment.db_content
-
end
-
-
1
def file_ready_to_save?
-
9
attachment.file.present? &&
-
!preliminary_upload? &&
-
!save_preliminary_upload? &&
-
attachment_is_changing?
-
end
-
-
1
def item_names _args={} # needed for flexmail attachments. hacky.
-
[name]
-
end
-
-
1
def original_filename
-
18
return content.split("/").last if web?
-
18
attachment.original_filename
-
end
-
-
1
def unfilled?
-
3
!attachment.present? && !save_preliminary_upload? && !subcards? && blank_content?
-
end
-
-
1
def attachment_changed?
-
send "#{attachment_name}_changed?"
-
end
-
-
1
def attachment_is_changing?
-
1
send "#{attachment_name}_is_changing?"
-
end
-
-
1
def attachment_before_act
-
send "#{attachment_name}_before_act"
-
end
-
-
1
def create_versions? _new_file
-
true
-
end
-
-
1
def empty_ok?
-
6
@empty_ok
-
end
-
-
1
def assign_set_specific_attributes
-
# reset content if we really have something to upload
-
1306
self.content = nil if set_specific[attachment_name.to_s].present?
-
1306
super
-
end
-
-
1
def delete_files_for_action action
-
9
with_selected_action_id(action.id) do
-
9
attachment.file.delete
-
9
attachment.versions.each_value do |version|
-
28
version.file.delete
-
end
-
end
-
end
-
-
1
def revision action, before_action=false
-
return unless (result = super)
-
result[:empty_ok] = true
-
result
-
end
-
-
1
def attachment_format ext
-
30
if ext.present? && attachment && (original_ext = attachment.extension.sub(/^\./, ""))
-
30
if ["file", original_ext].member? ext
-
30
original_ext
-
elsif (exts = Mime::Types[attachment.content_type])
-
if exts.find { |mt| mt.extensions.member? ext }
-
ext
-
else
-
exts[0].extensions[0]
-
end
-
end
-
end
-
rescue => e
-
Rails.logger.info "attachment_format issue: #{e.message}"
-
nil
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Abstract; module Attachment;
-
# Set: Abstract (Attachment, Cloud)
-
#
-
1
module Cloud;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/cloud.rb"; end
-
1
event :change_bucket_if_read_only, :initialize,
-
on: :update, when: :change_bucket_if_read_only? do
-
@new_storage_type = storage_type_from_config
-
end
-
-
1
event :validate_storage_type_update, :validate, on: :update, when: :cloud? do
-
# FIXME: make it possible to retrieve the file from cloud storage
-
# to store it somewhere else. Currently, it only works to change the
-
# storage type if a new file is provided
-
# i.e. `update storage_type: :local` fails but
-
# `update storage_type: :local, file: [file handle]` is ok
-
return unless storage_type_changed? && !attachment_is_changing?
-
-
errors.add :storage_type, tr(:moving_files_is_not_supported)
-
end
-
-
1
def bucket
-
554
@bucket ||= cloud? && (new_card_bucket || bucket_from_content || bucket_from_config)
-
end
-
-
1
def new_card_bucket
-
return unless new_card?
-
# If the file is assigned before the bucket option we have to
-
# check if there is a bucket options in set_specific.
-
# That happens for exmaple when the file appears before the bucket in the
-
# options hash:
-
# Card.create file: file_handle, bucket: "my_bucket"
-
set_specific[:bucket] || set_specific["bucket"] || bucket_from_config
-
end
-
-
1
def bucket_config
-
@bucket_config ||= load_bucket_config
-
end
-
-
1
def load_bucket_config
-
return {} unless bucket
-
bucket_config = Cardio.config.file_buckets&.dig(bucket.to_sym) || {}
-
bucket_config.symbolize_keys!
-
bucket_config[:credentials]&.symbolize_keys!
-
# we don't want :attributes hash symbolized, so we can't use
-
# deep_symbolize_keys
-
ensure_bucket_config do
-
load_bucket_config_from_env bucket_config
-
end
-
end
-
-
1
def ensure_bucket_config
-
yield.tap do |config|
-
require_configuration! config
-
require_credentials! config
-
end
-
end
-
-
1
def require_configuration! config
-
cant_find_in_bucket! "configuration" unless config.present?
-
end
-
-
1
def require_credentials! config
-
cant_find_in_bucket! "credentials" unless config[:credentials]
-
end
-
-
1
def cant_find_in_bucket! need
-
raise Card::Error, "couldn't find #{need} for bucket #{bucket}"
-
end
-
-
1
def load_bucket_config_from_env config
-
config ||= {}
-
each_config_option_from_env do |key|
-
replace_with_env_variable config, key
-
end
-
credential_config config do |cred_hash|
-
load_bucket_credentials_from_env cred_hash
-
end
-
end
-
-
1
def credential_config config
-
config[:credentials] ||= {}
-
yield config[:credentials]
-
config.delete :credentials if config[:credentials].blank?
-
config
-
end
-
-
1
def each_config_option_from_env
-
CarrierWave::FileCardUploader::CONFIG_OPTIONS.each do |key|
-
yield key unless key.in? %i[attributes credentials]
-
end
-
end
-
-
1
def load_bucket_credentials_from_env cred_config
-
each_credential_from_env do |option|
-
replace_with_env_variable cred_config, option, "credentials"
-
end
-
end
-
-
1
def each_credential_from_env
-
regexp = credential_from_env_regexp
-
ENV.each_key do |env_key|
-
next unless (m = regexp.match env_key)
-
yield m[:option].downcase.to_sym
-
end
-
end
-
-
1
def credential_from_env_regexp
-
Regexp.new "^(?:#{bucket.to_s.upcase}_)?CREDENTIALS_(?<option>.+)$"
-
end
-
-
1
def replace_with_env_variable config, option, prefix=nil
-
env_key = [prefix, option].compact.join("_").upcase
-
new_value = ENV["#{bucket.to_s.upcase}_#{env_key}"] || ENV[env_key]
-
config[option] = new_value if new_value
-
end
-
-
1
def bucket_from_content
-
return unless content
-
content.match(/^\((?<bucket>[^)]+)\)/) { |m| m[:bucket] }
-
end
-
-
1
def bucket_from_config
-
cnf = Cardio.config
-
cnf.file_default_bucket || cnf.file_buckets&.keys&.first
-
end
-
-
1
def change_bucket_if_read_only?
-
7
cloud? && bucket_config[:read_only] && attachment_is_changing?
-
end
-
-
1
def bucket= value
-
if @action == :update
-
@new_bucket = value
-
else
-
@bucket = value
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/cloud.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Abstract; module Attachment;
-
# Set: Abstract (Attachment, Coded)
-
#
-
1
module Coded;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/coded.rb"; end
-
1
event :lose_coded_status_on_update, :initialize, on: :update, when: :coded? do
-
# unless explicit
-
return if @new_mod
-
@new_storage_type ||= storage_type_from_config
-
end
-
-
1
event :validate_coded_storage_type, :validate, on: :save, when: :will_become_coded? do
-
errors.add :storage_type, tr(:mod_argument_needed_to_save) unless mod || @new_mod
-
errors.add :storage_type, tr(:codename_needed_for_storage) if codename.blank?
-
end
-
-
1
def will_become_coded?
-
83
will_be_stored_as == :coded
-
end
-
-
1
def mod= value
-
if @action == :update && mod != value
-
@new_mod = value.to_s
-
else
-
@mod = value.to_s
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/coded.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Abstract; module Attachment;
-
# Set: Abstract (Attachment, Local)
-
#
-
1
module Local;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/local.rb"; end
-
-
1
event :update_public_link_on_create, :integrate, on: :create, when: :local? do
-
5
update_public_link
-
end
-
-
1
event :remove_public_link_on_delete, :integrate, on: :delete, when: :local? do
-
remove_public_links
-
end
-
-
1
event :update_public_link, after: :update_read_rule, when: :local? do
-
5
return if content.blank?
-
5
if who_can(:read).include? Card::AnyoneID
-
5
create_public_links
-
else
-
remove_public_links
-
end
-
end
-
-
1
private
-
-
1
def create_public_links
-
5
path = attachment.public_path
-
5
return if File.exist? path
-
5
FileUtils.mkdir_p File.dirname(path)
-
5
File.symlink attachment.path, path unless File.symlink? path
-
5
create_versions_public_links
-
end
-
-
1
def create_versions_public_links
-
5
attachment.versions.each_value do |version|
-
16
next if File.symlink? version.public_path
-
16
File.symlink version.path, version.public_path
-
end
-
end
-
-
1
def remove_public_links
-
symlink_dir = File.dirname attachment.public_path
-
return unless Dir.exist? symlink_dir
-
FileUtils.rm_rf symlink_dir
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/local.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Abstract; module Attachment;
-
# Set: Abstract (Attachment, Paths)
-
#
-
1
module Paths;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/paths.rb"; end
-
1
MOD_FILE_DIR = "file".freeze
-
-
1
def store_dir
-
73
will_become_coded? ? coded_dir(@new_mod) : upload_dir
-
end
-
-
1
def retrieve_dir
-
2451
coded? ? coded_dir : upload_dir
-
end
-
-
# place for files of regular file cards
-
1
def upload_dir
-
371
id ? "#{files_base_dir}/#{id}" : tmp_upload_dir
-
end
-
-
# place for files of mod file cards
-
1
def coded_dir new_mod=nil
-
2153
dir = File.join mod_dir(new_mod), MOD_FILE_DIR, codename.to_s
-
2153
FileUtils.mkdir_p(dir) unless File.directory?(dir)
-
2153
dir
-
end
-
-
1
def mod_dir new_mod=nil
-
2153
mod_name = new_mod || mod
-
2153
dir = Mod.dirs.path(mod_name) || (mod_name.to_sym == :test && "test")
-
-
2153
raise Error, "can't find mod \"#{mod_name}\"" unless dir
-
2153
dir
-
end
-
-
1
def files_base_dir
-
554
dir = bucket ? bucket_config[:subdirectory] : Card.paths["files"].existent.first
-
554
dir || files_base_dir_configuration_error
-
end
-
-
1
def files_base_dir_configuration_error
-
raise StandardError,
-
"missing directory for file cache (default is `files` in deck root)"
-
end
-
-
# used in the indentifier
-
1
def file_dir
-
1226
if coded?
-
1136
":#{codename}"
-
90
elsif cloud?
-
"(#{bucket})/#{file_id}"
-
else
-
90
"~#{file_id}"
-
end
-
end
-
-
1
def public?
-
who_can(:read).include? Card::AnyoneID
-
end
-
-
1
def file_id
-
90
id? ? id : upload_cache_card.id
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/paths.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Abstract; module Attachment;
-
# Set: Abstract (Attachment, StorageType)
-
#
-
1
module StorageType;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/storage_type.rb"; end
-
1
attr_writer :bucket, :storage_type
-
-
1
event :storage_type_change, :store, on: :update, when: :storage_type_changed? do
-
# carrierwave stores file if @cache_id is not nil
-
attachment.cache_stored_file!
-
# attachment.retrieve_from_cache!(attachment.cache_name)
-
update_storage_attributes
-
# next line might be necessary to move files to cloud
-
-
# make sure that we get the new identifier
-
# otherwise action_id will return wrong id for new identifier
-
db_content_will_change!
-
write_identifier
-
end
-
-
1
event :validate_storage_type, :validate, on: :save do
-
10
unless known_storage_type? will_be_stored_as
-
errors.add :storage_type, tr(
-
:unknown_storage_type,
-
new_storage_type: @new_storage_type
-
)
-
end
-
end
-
-
1
def will_be_stored_as
-
99
@new_storage_type || storage_type
-
end
-
-
1
def read_only?
-
1
web? || (cloud? && bucket_config[:read_only])
-
end
-
-
1
def cloud?
-
1881
storage_type == :cloud
-
end
-
-
1
def web?
-
1586
storage_type == :web
-
end
-
-
1
def local?
-
5
storage_type == :local
-
end
-
-
1
def coded?
-
8625
storage_type == :coded
-
end
-
-
1
def remote_storage?
-
30
cloud? || web?
-
end
-
-
1
def storage_type
-
14742
@storage_type ||=
-
1207
new_card? ? storage_type_from_config : storage_type_from_content
-
end
-
-
1
def deprecated_mod_file?
-
4
content && (lines = content.split("\n")) && lines.size == 4
-
end
-
-
1
def mod
-
3289
@mod ||= coded? && mod_from_content
-
end
-
-
1
def mod_from_content
-
1149
if content =~ %r{^:[^/]+/([^.]+)}
-
1149
Regexp.last_match(1) # current mod_file format
-
else
-
mod_from_deprecated_content
-
end
-
end
-
-
# old format is still used in card_changes
-
1
def mod_from_deprecated_content
-
return if content =~ /^\~/
-
return unless (lines = content.split("\n")) && lines.size == 4
-
lines.last
-
end
-
-
1
def storage_type_from_config
-
32
valid_storage_type ENV["FILE_STORAGE"] || Cardio.config.file_storage
-
end
-
-
1
def valid_storage_type storage_type
-
32
storage_type.to_sym.tap do |type|
-
32
invalid_storage_type! type unless type.in? valid_storage_type_list
-
end
-
end
-
-
1
def valid_storage_type_list
-
32
CarrierWave::FileCardUploader::STORAGE_TYPES
-
end
-
-
1
def invalid_storage_type! type
-
raise Card::Error, tr(:error_invalid_storage_type, type: type)
-
end
-
-
1
def storage_type_from_content
-
1189
case content
-
when /^\(/ then :cloud
-
when %r{/^https?\:/} then :web
-
36
when /^~/ then :local
-
1149
when /^\:/ then :coded
-
else
-
4
if deprecated_mod_file?
-
:coded
-
else
-
4
storage_type_from_config
-
end
-
end
-
end
-
-
1
def update_storage_attributes
-
@mod = @new_mod if @new_mod
-
@bucket = @new_bucket if @new_bucket
-
@storage_type = @new_storage_type
-
end
-
-
1
def storage_type_changed?
-
4
@new_bucket || (@new_storage_type && @new_storage_type != storage_type) || @new_mod
-
end
-
-
1
def storage_type= value
-
known_storage_type? value
-
if @action == :update #&& storage_type != value
-
# we cant update the storage type directly here
-
# if we do then the uploader doesn't find the file we want to update
-
@new_storage_type = value
-
else
-
@storage_type = value
-
end
-
end
-
-
1
def with_storage_options opts={}
-
1257
old_values = {}
-
1257
validate_temporary_storage_type_change opts[:storage_type]
-
1257
%i[storage_type mod bucket].each do |opt_name|
-
3771
next unless opts[opt_name]
-
old_values[opt_name] = instance_variable_get "@#{opt_name}"
-
instance_variable_set "@#{opt_name}", opts[opt_name]
-
@temp_storage_type = true
-
end
-
1257
yield
-
ensure
-
1257
@temp_storage_type = false
-
1257
old_values.each do |key, val|
-
instance_variable_set "@#{key}", val
-
end
-
end
-
-
1
def temporary_storage_type_change?
-
1040
@temp_storage_type
-
end
-
-
1
def validate_temporary_storage_type_change new_storage_type=nil
-
1257
new_storage_type ||= @new_storage_type
-
1257
return unless new_storage_type
-
unless known_storage_type? new_storage_type
-
raise Error, tr(:unknown_storage_type, new_storage_type: new_storage_type)
-
end
-
-
if new_storage_type == :coded && codename.blank?
-
raise Error, "codename needed for storage type :coded"
-
end
-
end
-
-
1
def known_storage_type? type=storage_type
-
10
type.in? CarrierWave::FileCardUploader::STORAGE_TYPES
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/storage_type.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Abstract; module Attachment;
-
# Set: Abstract (Attachment, UploadCache)
-
#
-
# action id of the cached upload
-
1
module UploadCache;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/upload_cache.rb"; end
-
1
attr_accessor :action_id_of_cached_upload
-
-
1
def actionable?
-
28
super || preliminary_upload?
-
end
-
-
1
event :prepare_attachment, :prepare_to_validate, on: :save, when: :preliminary_upload? do
-
9
save_original_filename # save original filename as comment in action
-
9
write_identifier # set db_content
-
# (needs original filename to determine extension)
-
9
store_attachment!
-
9
store_card_changes
-
# finalize_action # create Card::Change entry for db_content
-
-
9
card_id = new_card? ? upload_cache_card.id : id
-
9
@current_action.update! draft: true, card_id: card_id
-
9
success << {
-
9
target: (new_card? ? upload_cache_card : self),
-
type: type_name,
-
view: "preview_editor",
-
rev_id: current_action.id
-
}
-
9
abort :success
-
end
-
-
1
event :assign_attachment_on_create, :initialize,
-
after: :assign_action, on: :create, when: :save_preliminary_upload? do
-
5
return unless (action = Card::Action.fetch(@action_id_of_cached_upload))
-
5
upload_cache_card.selected_action_id = action.id
-
5
upload_cache_card.select_file_revision
-
5
assign_attachment upload_cache_card.attachment.file, action.comment
-
end
-
-
1
event :assign_attachment_on_update, :initialize,
-
after: :assign_action, on: :update, when: :save_preliminary_upload? do
-
3
return unless (action = Card::Action.fetch(@action_id_of_cached_upload))
-
6
uploaded_file = with_selected_action_id(action.id) { attachment.file }
-
3
assign_attachment uploaded_file, action.comment
-
end
-
-
1
def assign_attachment file, original_filename
-
8
send "#{attachment_name}=", file
-
8
write_identifier
-
8
@current_action&.update! comment: original_filename
-
end
-
-
1
event :delete_cached_upload_file_on_create, :integrate,
-
on: :create, when: :save_preliminary_upload? do
-
5
return unless (action = Card::Action.fetch(@action_id_of_cached_upload))
-
5
upload_cache_card.delete_files_for_action action
-
5
action.delete
-
end
-
-
# at some point uploaded files of canceled file card creation
-
# should be deleted. We do this when ever an new file is created.
-
1
event :clear_draft_files, :integrate_with_delay, priority: 100, on: :create do
-
5
Card.delete_tmp_files_of_cached_uploads
-
end
-
-
1
event :delete_cached_upload_file_on_update, :integrate,
-
on: :update, when: :save_preliminary_upload? do
-
3
return unless (action = Card::Action.fetch(@action_id_of_cached_upload))
-
3
delete_files_for_action action
-
3
action.delete
-
end
-
-
# used for uploads for new cards until the new card is created
-
1
def upload_cache_card
-
71
cache_card_codename = "new_#{attachment_name}"
-
71
@upload_cache_card ||= Card::Codename.card(cache_card_codename) { Card[:new_file] }
-
end
-
-
1
def preliminary_upload?
-
32
Card::Env && Card::Env.params[:attachment_upload]
-
end
-
-
1
def save_preliminary_upload?
-
37
@action_id_of_cached_upload.present?
-
end
-
-
# place for files if card doesn't have an id yet
-
1
def tmp_upload_dir _action_id=nil
-
22
"#{files_base_dir}/#{upload_cache_card.id}"
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/upload_cache.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Abstract; module Attachment;
-
# Set: Abstract (Attachment, Web)
-
#
-
1
module Web;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/web.rb"; end
-
1
def no_upload?
-
10
web? || storage_type_from_config == :web
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/web.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (FileUtils)
-
#
-
1
module FileUtils;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/all/file_utils.rb"; end
-
1
module ClassMethods
-
1
def update_all_storage_locations
-
Card.search(type_id: ["in", Card::FileID, Card::ImageID])
-
.each(&:update_storage_location!)
-
end
-
-
1
def delete_tmp_files_of_cached_uploads
-
5
cards_with_disposable_attachments do |card, action|
-
1
card.delete_files_for_action action
-
1
action.delete
-
end
-
end
-
-
1
def cards_with_disposable_attachments
-
5
draft_actions_with_attachment.each do |action|
-
# we don't want to delete uploads in progress
-
1
next unless old_enough?(action.created_at) && (card = action.card)
-
# we can't delete attachments we don't have write access to
-
1
next if card.read_only?
-
-
1
yield card, action
-
end
-
end
-
-
1
def old_enough? time, expiration_time=5.day.to_i
-
1
Time.now - time > expiration_time
-
end
-
-
1
def draft_actions_with_attachment
-
5
Card::Action.find_by_sql(
-
"SELECT * FROM card_actions "\
-
"INNER JOIN cards ON card_actions.card_id = cards.id "\
-
"WHERE cards.type_id IN (#{Card::FileID}, #{Card::ImageID}) "\
-
"AND card_actions.draft = true"
-
)
-
end
-
-
1
def count_cards_with_attachment
-
Card.search type_id: ["in", Card::FileID, Card::ImageID], return: :count
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/all/file_utils.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Admin"
-
#
-
1
module Admin;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/self/admin.rb"; end
-
1
add_to_basket(
-
:tasks,
-
name: :update_file_storage_locations,
-
execute_policy: -> { Card.update_all_storage_locations },
-
stats: {
-
title: "cards with attachment",
-
count: -> { Card.count_cards_with_attachment }
-
# link_text: "update storage locations",
-
# task: "update_file_storage_locations"
-
}
-
)
-
-
1
add_to_basket(
-
:tasks,
-
name: :delete_upload_tmp_files,
-
execute_policy: -> { Card.delete_tmp_files_of_cached_uploads },
-
stats: {
-
title: "tmp files of canceled uploads",
-
count: -> { ::Card.draft_actions_with_attachment },
-
link_text: "delete tmp files",
-
task: "delete_tmp_files_of_cached_uploads"
-
}
-
)
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/self/admin.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Favicon"
-
#
-
1
module Favicon;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/self/favicon.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :source do
-
224
source = card.type_id == Card::ImageID ? super() : nil
-
224
source.present? ? source : nest(:logo, view: :source, size: voo.size)
-
end
-
-
1
view :link_tag, perms: :none do
-
224
return unless (source = render :source, size: :small)
-
224
tag :link, rel: "shortcut icon", href: source
-
end
-
-
1
def raw_help_text
-
"A favicon (or shortcut icon) is a small image used by browsers to help identify "\
-
"your website. [[http://www.decko.org/favicon|How to customize your favicon]]"
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/self/favicon.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "NewFile"
-
#
-
1
module NewFile;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/self/new_file.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :source, cache: :never do
-
super()
-
end
-
-
1
view :core, cache: :never do
-
super()
-
end
-
-
1
view :input, cache: :never do
-
super()
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/self/new_file.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "NewImage"
-
#
-
1
module NewImage;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/self/new_image.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :source, cache: :never do
-
4
super()
-
end
-
-
1
view :core, cache: :never do
-
4
super()
-
end
-
-
1
view :input, cache: :never do
-
super()
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/self/new_image.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "File" cards
-
#
-
1
module File;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/type/file.rb"; end
-
1
attachment :file, uploader: CarrierWave::FileCardUploader
-
-
1
module SelectedAction
-
1
def select_action_by_params params
-
# skip action table lookups for current revision
-
51
rev_id = params[:rev_id]
-
51
super unless rev_id && rev_id == last_content_action_id
-
end
-
-
1
def last_content_action_id
-
1040
return super if temporary_storage_type_change?
-
# find action id from content (saves lookups)
-
1040
db_content.to_s.split(%r{[/\.]})[-2]
-
end
-
end
-
1
include SelectedAction
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
view :source do
-
1
file = card.attachment
-
1
return "" unless file.valid?
-
1
contextualize_path file.url
-
end
-
-
1
view :core do
-
handle_source do |source|
-
card_url source
-
end
-
end
-
-
1
def short_content
-
number_to_human_size card.attachment.size
-
end
-
-
1
def handle_source
-
38
source = _render_source
-
38
return "" if source.blank?
-
38
block_given? ? yield(source) : source
-
rescue => e
-
Rails.logger.info "Error with file source: #{e.message}"
-
tr :file_error
-
end
-
-
1
def selected_version
-
2
card.attachment
-
end
-
end
-
-
2
module FileFormat; module_parent.send :register_set_format, Card::Format::FileFormat, self; extend Card::Set::AbstractFormat
-
# NOCACHE because returns send_file args. not in love with this...
-
1
view :core, cache: :never do
-
# this means we only support known formats. dislike.
-
30
attachment_format = card.attachment_format(params[:format])
-
30
return _render_not_found unless attachment_format
-
30
return card.format(:html).render_core if card.remote_storage?
-
30
set_response_headers
-
30
args_for_send_file
-
end
-
-
1
def args_for_send_file
-
30
file = selected_version
-
30
[file.path, { type: file.content_type,
-
filename: "#{card.name.safe_key}#{file.extension}",
-
x_sendfile: true,
-
30
disposition: (params[:format] == "file" ? "attachment" : "inline") }]
-
end
-
-
1
def set_response_headers
-
30
return unless params[:explicit_file] && (response = controller&.response)
-
30
response.headers["Expires"] = 1.year.from_now.httpdate
-
# currently using default "private", because proxy servers could block
-
# needed permission checks
-
# r.headers["Cache-Control"] = "public"
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
1
handle_source do |source|
-
1
"<a href=\"#{source}\">#{tr :download, title: title_in_context(voo.title)}</a>"
-
end
-
end
-
-
1
view :input do
-
10
if card.no_upload?
-
text_field :content, class: "d0-card-content"
-
else
-
10
haml :file_chooser, action_text: file_chooser_action_text
-
end
-
end
-
-
1
view :preview_editor, unknown: true, cache: :never do
-
9
haml :preview_editor
-
end
-
-
1
def file_chooser_action_text
-
10
action = card.new_card? ? "Add" : "Replace"
-
10
"#{action} #{humanized_attachment_name}..."
-
end
-
-
1
def humanized_attachment_name
-
10
card.attachment_name.to_s.humanize
-
end
-
-
1
def preview
-
4
""
-
end
-
-
1
def cached_upload_card_name
-
9
Card::Env.params[:attachment_upload].gsub(/\[\w+\]$/, "[action_id_of_cached_upload]")
-
end
-
-
1
def preview_editor_delete_text
-
9
tr :delete
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/type/file.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Image" cards
-
#
-
1
module Image;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/type/image.rb"; end
-
1
attachment :image, uploader: CarrierWave::ImageCardUploader
-
-
1
include File::SelectedAction
-
-
1
def create_versions? new_file
-
2356
new_file.extension != "svg"
-
end
-
-
1
def svg?
-
261
image&.extension == ".svg"
-
end
-
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
include File::Format
-
-
1
view :one_line_content do
-
_render_core size: :icon
-
end
-
-
1
def short_content
-
7
render_core size: :icon
-
end
-
-
1
view :source do
-
261
return card.content if card.web?
-
261
image = selected_version
-
261
return "" unless image.valid?
-
261
contextualize_path image.url
-
end
-
-
1
def selected_version
-
289
size = determine_image_size
-
289
if size && size != :original
-
289
card.image.versions[size]
-
else
-
card.image
-
end
-
end
-
-
1
def handle_source
-
37
super
-
end
-
-
1
def closed_size
-
:icon
-
end
-
-
1
def main_size
-
7
:large
-
end
-
-
1
def default_size
-
5
:medium
-
end
-
-
1
def determine_image_size
-
289
voo.size =
-
case
-
when nest_mode == :closed then closed_size
-
277
when voo.size.present? then voo.size.to_sym
-
7
when main? then main_size
-
5
else default_size
-
end
-
289
voo.size = :original if voo.size == :full
-
289
voo.size
-
end
-
-
1
view :inline do
-
_render_core
-
end
-
end
-
-
2
module EmailHtmlFormat; module_parent.send :register_set_format, Card::Format::EmailHtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :inline, cache: :never do
-
handle_source do |source|
-
return source unless (mail = inherit :active_mail) &&
-
::File.exist?(path = selected_version.path)
-
url = attach_image mail, path
-
image_tag url
-
end
-
end
-
-
1
def attach_image mail, path
-
mail.attachments.inline[path] = ::File.read path
-
mail.attachments[path].url
-
end
-
end
-
-
2
module CssFormat; module_parent.send :register_set_format, Card::Format::CssFormat, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
handle_source
-
end
-
-
1
view :content do # why is this necessary?
-
render_core
-
end
-
end
-
-
2
module FileFormat; module_parent.send :register_set_format, Card::Format::FileFormat, self; extend Card::Set::AbstractFormat
-
1
include File::FileFormat
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/type/image.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Type; module Image;
-
# Set: All "Image+HtmlViews" cards (HtmlViews)
-
#
-
1
module HtmlViews;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/type/image/html_views.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
include File::HtmlFormat
-
-
# core HTML image view.
-
1
view :core do
-
261
return card.attachment.read if card.svg?
-
37
with_valid_source do |source|
-
37
image_tag source, alt: card.name
-
end
-
end
-
-
1
def with_valid_source
-
37
handle_source do |source|
-
37
if source.blank? || source == "missing"
-
# FIXME: these images should be "broken", not "missing"
-
invalid_image source
-
else
-
37
yield source
-
# consider title..
-
end
-
end
-
end
-
-
1
view :full_width do
-
with_valid_source do |source|
-
image_tag source, alt: card.name, class: "w-100"
-
end
-
end
-
-
1
def invalid_image source
-
# ("missing" is the view for "unknown" now, so we shouldn't further confuse things)
-
"<!-- invalid image for #{safe_name}; source: #{source} -->"
-
end
-
-
1
def preview
-
15
return if card.new_card? && !card.preliminary_upload?
-
11
wrap_with :div, class: "attachment-preview",
-
id: "#{card.attachment.filename}-preview" do
-
11
_render_core size: :medium
-
end
-
end
-
-
1
def show_action_content_toggle? _action, _view_type
-
true
-
end
-
-
1
view :content_changes do
-
content_changes card.last_action, :expanded
-
end
-
-
1
def content_changes action, diff_type, hide_diff=false
-
voo.size = diff_type == :summary ? :icon : :medium
-
[old_image(action, hide_diff), new_image(action)].compact.join
-
end
-
-
1
def old_image action, hide_diff
-
return if hide_diff || !action
-
return unless (last_change = card.last_change_on(:db_content, before: action))
-
card.with_selected_action_id last_change.card_action_id do
-
Card::Content::Diff.render_deleted_chunk _render_core
-
end
-
end
-
-
1
def new_image action
-
card.with_selected_action_id action.id do
-
Card::Content::Diff.render_added_chunk _render_core
-
end
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/type/image/html_views.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Calendar)
-
#
-
1
module Calendar;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-date/set/all/calendar.rb"; end
-
1
Self::InputOptions.add_to_basket :options, "calendar"
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def calendar_input
-
text_field :content, class: "date-editor datetimepicker-input",
-
"data-toggle": "datetimepicker",
-
"data-target": "##{form_prefix}_content.date-editor"
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-date/set/all/calendar.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Datepicker"
-
#
-
1
module Datepicker;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-date/set/self/datepicker.rb"; end
-
1
def raw_help_text
-
<<-TEXT
-
Configure the date select tool using these available
-
[[https://tempusdominus.github.io/bootstrap-4/Options/|options]]
-
TEXT
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-date/set/self/datepicker.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "ScriptDatepicker"
-
#
-
1
module ScriptDatepicker;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-date/set/self/script_datepicker.rb"; end
-
1
include_set Abstract::VendorCodeFile
-
1
Self::ScriptMods.add_item :script_datepicker
-
-
1
def source_files
-
%w[moment/min/moment-with-locales.min.js
-
tempusdominus/build/js/tempusdominus-bootstrap-4.js]
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-date/set/self/script_datepicker.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "ScriptDatepickerConfig"
-
#
-
1
module ScriptDatepickerConfig;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-date/set/self/script_datepicker_config.rb"; end
-
1
include_set Abstract::CodeFile
-
-
1
Self::ScriptEditors.add_item :script_datepicker_config
-
1
All::Head::HtmlFormat.add_to_basket :mod_js_config, [:datepicker, "setDatepickerConfig"]
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-date/set/self/script_datepicker_config.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "StyleDatepicker"
-
#
-
1
module StyleDatepicker;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-date/set/self/style_datepicker.rb"; end
-
1
include_set Abstract::CodeFile
-
1
Self::StyleLibraries.add_item :style_datepicker
-
-
1
def source_files
-
%w[lib/stylesheets/tempusdominus.scss
-
vendor/tempusdominus/src/sass/_tempusdominus-bootstrap-4.scss]
-
end
-
-
1
def source_dir
-
""
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-date/set/self/style_datepicker.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Date" cards
-
#
-
1
module Date;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-date/set/type/date.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def input_type
-
:calendar
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-date/set/type/date.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Abstract
-
# Set: Abstract (FollowOption)
-
#
-
1
module FollowOption;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/abstract/follow_option.rb"; end
-
1
def restrictive_option?
-
Card::FollowOption.restrictive_options.include? codename
-
end
-
-
1
def description set_card
-
34
set_card.follow_label
-
end
-
-
# follow option methods on the Card class
-
# FIXME: there's not a great reason to have these on the Card class
-
1
module ClassMethods
-
# args:
-
# position: <Fixnum> (starting at 1, default: add to end)
-
1
def restrictive_follow_opts args
-
2
add_option args, :restrictive
-
end
-
-
# args:
-
# position: <Fixnum> (starting at 1, default: add to end)
-
1
def follow_opts args
-
2
add_option args, :main
-
end
-
-
1
def follow_test opts={}, &block
-
2
Card::FollowOption.test[get_codename(opts)] = block
-
end
-
-
1
def follower_candidate_ids opts={}, &block
-
2
Card::FollowOption.follower_candidate_ids[get_codename(opts)] = block
-
end
-
-
1
private
-
-
1
def insert_option pos, item, type
-
4
list = Card::FollowOption.codenames(type)
-
4
list[pos] ? list.insert(pos, item) : (list[pos] = item)
-
# If pos > codenames.size in a previous insert then we have a bunch
-
# of preceding nils in the array.
-
# Hence, we have to overwrite a nil value if we encounter one and
-
# can't use insert.
-
end
-
-
1
def add_option opts, type, &_block
-
4
codename = get_codename opts
-
4
if opts[:position]
-
4
insert_option opts[:position] - 1, codename, type
-
else
-
Card::FollowOption.codenames(type) << codename
-
end
-
4
Card::FollowOption.codenames(:all) << codename
-
end
-
-
1
def get_codename opts
-
8
opts[:codename] || name.match(/::(\w+)$/)[1].underscore.to_sym
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/abstract/follow_option.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Follow)
-
#
-
# for override
-
1
module Follow;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow.rb"; end
-
1
def followable?
-
89
true
-
end
-
-
1
def follow_label
-
name
-
end
-
-
1
def list_direct_followers?
-
false
-
end
-
-
1
def follow_option?
-
codename && FollowOption.codenames.include?(codename)
-
end
-
-
# the set card to be followed if you want to follow changes of card
-
1
def follow_set_card
-
4
Card.fetch name, :self
-
end
-
-
1
def follow_rule_name user=nil
-
follow_set_card&.follow_rule_name user
-
end
-
-
1
def follow_rule_card user=nil, args={}
-
Card.fetch follow_rule_name(user), args
-
end
-
-
1
def follow_rule? user=nil
-
Card.exists? follow_rule_name(user)
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Follow)
-
#
-
#! no set module
-
1
module Follow;
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/follow_link.rb"; end
-
1
class FollowLink
-
1
attr_reader :format, :rule_content, :link_text, :action, :css_class, :hover_text
-
-
1
delegate :link_to, to: :format
-
-
1
def initialize format
-
@format = format
-
@card = format.card
-
end
-
-
1
def modal_link icon=false
-
opts = link_opts.merge(
-
"data-path": link_opts[:path],
-
"data-toggle": "modal",
-
"data-target": "#modal-#{card.name.safe_key}",
-
class: css_classes("follow-link", link_opts[:class])
-
)
-
link_to render_link_text(icon), opts
-
end
-
-
1
def button
-
opts = link_opts(:follow_section).merge(
-
remote: true,
-
class: @format.css_classes("follow-link", link_opts[:class],
-
"slotter btn btn-sm btn-primary")
-
)
-
opts["data-update-foreign-slot"] = ".d0-card-body > .card-slot.RIGHT-Xfollower.content-view"
-
opts["data-hover-text"] = hover_text if hover_text
-
link_to render_link_text, opts
-
end
-
-
1
def link_opts success_view=:follow_status
-
{ title: title,
-
path: path(success_view),
-
class: css_class }
-
end
-
-
1
def render_link_text icon=false
-
verb = %(<span class="follow-verb">#{link_text}</span>)
-
icon = icon ? icon_tag(:flag) : ""
-
[icon, verb].compact.join.html_safe
-
end
-
-
1
private
-
-
1
def mark
-
@card.follow_set_card.follow_rule_name Auth.current.name
-
end
-
-
1
def path view=:follow_status
-
@format.path mark: mark,
-
action: :update,
-
success: { id: @card.name, view: view },
-
card: { content: "[[#{rule_content}]]" }
-
end
-
-
1
def title
-
"#{action} emails about changes to #{@card.follow_label}"
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/follow_link.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module Follow;
-
# Set: All cards (Follow, FollowLinkViews)
-
#
-
1
module FollowLinkViews;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/follow_link_views.rb"; end
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
def follow_link_class
-
card.followed? ? StopFollowLink : StartFollowLink
-
end
-
-
1
def show_follow?
-
Auth.signed_in? && !card.new_card? && card.followable?
-
end
-
end
-
-
2
module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
-
1
view :follow_status do
-
follow_link_class.link_opts
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def follow_button
-
follow_link_class.new(self).button
-
end
-
-
1
def follow_modal_link
-
follow_link_class.new(self).modal_link
-
end
-
-
1
view :follow_button, cache: :never do
-
follow_button
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/follow_link_views.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module Follow;
-
# Set: All cards (Follow, FollowedBy)
-
#
-
# used by +:followers overwritten in type/set.rb and type/cardtype.rb
-
1
module FollowedBy;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/followed_by.rb"; end
-
1
def followed?
-
followed_by? Auth.current_id
-
end
-
-
# for sets and cardtypes it doesn't check whether the users is following the
-
# card itself instead it checks whether he is following the complete set
-
1
def followed_by? user_id
-
follow_rule_applies?(user_id) || left&.followed_by_as_field?(self, user_id)
-
end
-
-
1
def followed_by_as_field? field, user_id
-
followed_field?(field) && followed_by?(user_id)
-
end
-
-
# returns true if according to the follow_field_rule followers of self also
-
# follow changes of field_card
-
1
def followed_field? field_card
-
return unless (follow_field_rule = rule_card(:follow_fields))
-
-
follow_field_rule.item_names(context: self).find do |item|
-
case item.to_name.key
-
when field_card.key then true
-
when :nests.cardname.key then nested_card?(field_card)
-
end
-
end
-
end
-
-
1
def nested_card? card
-
nestee_ids.include? card.id
-
end
-
-
## the following methods all handle _explicit_ (direct) follow rules (not fields)
-
-
1
def follow_rule_applies? follower_id
-
!follow_rule_option(follower_id).nil?
-
end
-
-
1
def follow_rule_option follower_id
-
365
all_follow_rule_options(follower_id).find do |option|
-
700
follow_rule_option_applies? follower_id, option
-
end
-
end
-
-
1
def all_follow_rule_options follower_id
-
365
follow_rule = preference :follow, follower_id
-
365
return [] unless follow_rule.present?
-
-
365
follow_rule.split("\n")
-
end
-
-
1
def follow_rule_option_applies? follower_id, option
-
700
option_code = option.to_name.code
-
700
candidate_ids = follower_candidate_ids_for_option option_code
-
700
follow_rule_option_applies_to_candidates? follower_id, option_code, candidate_ids
-
end
-
-
1
def follow_rule_option_applies_to_candidates? follower_id, option_code, candidate_ids
-
700
if (test = FollowOption.test[option_code])
-
30
test.call follower_id, candidate_ids
-
else
-
670
candidate_ids.include? follower_id
-
end
-
end
-
-
1
def follower_candidate_ids_for_option option_code
-
700
return [] unless (block = FollowOption.follower_candidate_ids[option_code])
-
-
670
block.call self
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/followed_by.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module Follow;
-
# Set: All cards (Follow, FollowerIds)
-
#
-
1
module FollowerIds;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/follower_ids.rb"; end
-
1
FOLLOWER_IDS_CACHE_KEY = "FOLLOWER_IDS".freeze
-
-
1
card_accessor :followers
-
-
1
event :cache_expired_for_type_change, :store, on: :update, changed: %i[type_id name] do
-
1
act_card&.schedule_preference_expiration
-
# FIXME: expire (also?) after save
-
1
Card.follow_caches_expired
-
end
-
-
1
def schedule_preference_expiration
-
1
@expire_preferences_scheduled = true
-
end
-
-
1
def expire_preferences?
-
216
@expire_preferences_scheduled
-
end
-
-
1
event :expire_preferences_cache, :finalize, when: :expire_preferences? do
-
1
Card::Rule.clear_preference_cache
-
end
-
-
# follow cache methods on Card class
-
1
module ClassMethods
-
1
def follow_caches_expired
-
42
Card.clear_follower_ids_cache
-
42
Card::Rule.clear_preference_cache
-
end
-
-
1
def follower_ids_cache
-
12
Card.cache.read(FOLLOWER_IDS_CACHE_KEY) || {}
-
end
-
-
1
def write_follower_ids_cache hash
-
4
Card.cache.write FOLLOWER_IDS_CACHE_KEY, hash
-
end
-
-
1
def clear_follower_ids_cache
-
42
Card.cache.write FOLLOWER_IDS_CACHE_KEY, nil
-
end
-
end
-
-
1
def write_follower_ids_cache user_ids
-
4
hash = Card.follower_ids_cache
-
4
hash[id] = user_ids
-
4
Card.write_follower_ids_cache hash
-
end
-
-
1
def read_follower_ids_cache
-
8
Card.follower_ids_cache[id]
-
end
-
-
1
def follower_names
-
8
followers.map(&:name)
-
end
-
-
1
def followers
-
8
follower_ids.map do |id|
-
16
Card.fetch(id)
-
end
-
end
-
-
1
def follower_ids
-
8
@follower_ids = read_follower_ids_cache || begin
-
4
result = direct_follower_ids + indirect_follower_ids
-
4
write_follower_ids_cache result
-
4
result
-
end
-
end
-
-
1
def followers_count
-
follower_ids.size
-
end
-
-
1
def indirect_follower_ids
-
4
result = ::Set.new
-
4
left_card = left
-
4
while left_card
-
result += left_card.direct_follower_ids if left_card.followed_field? self
-
left_card = left_card.left
-
end
-
4
result
-
end
-
-
# all users (cards) that "directly" follow this card
-
# "direct" means there is a follow rule that applies explicitly to this card.
-
# one can also "indirectly" follow cards by following parent cards or other
-
# cards that nest this one.
-
1
def direct_followers
-
direct_follower_ids.map do |id|
-
Card.fetch(id)
-
end
-
end
-
-
1
def direct_follower_ids &block
-
169
ids = ::Set.new
-
169
set_names.each do |set_name|
-
954
direct_follower_ids_for_set setcard_from_name(set_name), ids, &block
-
end
-
169
ids
-
end
-
-
1
def setcard_from_name set_name
-
954
Card.fetch set_name, new: { type_id: SetID }
-
end
-
-
1
def direct_follower_ids_for_set set_card, ids
-
954
set_card.all_user_ids_with_rule_for(:follow).each do |user_id|
-
368
next if ids.include?(user_id) || !(option = follow_rule_option user_id)
-
-
28
yield user_id, set_card, option if block_given?
-
28
ids << user_id
-
end
-
end
-
-
1
def each_direct_follower_id_with_reason
-
165
direct_follower_ids do |user_id, set_card, follow_option|
-
20
reason = follow_option.gsub(/[\[\]]/, "")
-
20
yield user_id, set_card: set_card, option: reason
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/follower_ids.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Follow)
-
#
-
#! no set module
-
1
module Follow;
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/start_follow_link.rb"; end
-
-
1
class StartFollowLink < FollowLink
-
1
def initialize format
-
@rule_content = "*always"
-
@link_text = "follow"
-
@action = "send"
-
@css_class = ""
-
super
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/start_follow_link.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Follow)
-
#
-
#! no set module
-
1
module Follow;
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/stop_follow_link.rb"; end
-
-
1
class StopFollowLink < FollowLink
-
1
def initialize format
-
@rule_content = "*never"
-
@link_text = "following"
-
@hover_text = "unfollow"
-
@action = "stop sending"
-
@css_class = "btn-item-delete"
-
super
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/stop_follow_link.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Notify)
-
#
-
1
module Notify;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/all/notify.rb"; end
-
1
attr_accessor :follower_stash
-
1
mattr_accessor :force_notifications
-
-
1
event :silence_notifications, :initialize, when: :silence_notifications? do
-
@silent_change = true
-
end
-
-
1
def silence_notifications?
-
475
!(Card::Env[:controller] || force_notifications)
-
end
-
-
1
event :notify_followers_after_save,
-
:integrate_with_delay, on: :save, when: :notable_change? do
-
85
notify_followers
-
end
-
-
# in the delete case we have to calculate the follower_stash beforehand
-
# but we can't pass the follower_stash through the ActiveJob queue.
-
# We have to deal with the notifications in the integrate phase instead of the
-
# integrate_with_delay phase
-
1
event :stash_followers, :store, on: :delete, when: :notable_change? do
-
1
act_card.follower_stash ||= FollowerStash.new
-
1
act_card.follower_stash.check_card self
-
end
-
-
1
event :notify_followers_after_delete, :integrate, on: :delete, when: :notable_change? do
-
1
notify_followers
-
end
-
-
1
def notify_followers
-
86
return unless (act = Card::Director.act)
-
-
86
act.reload
-
86
notify_followers_of act
-
end
-
-
1
def notable_change?
-
222
!silent_change? && current_act_card? &&
-
89
(Card::Auth.current_id != WagnBotID) && followable?
-
end
-
-
1
def silent_change?
-
376
silent_change
-
end
-
-
1
private
-
-
1
def notify_followers_of act
-
86
act_followers(act).each_follower_with_reason do |follower, reason|
-
18
next if !follower.account || (follower == act.actor)
-
-
17
notify_follower follower, act, reason
-
end
-
end
-
-
1
def notify_follower follower, act, reason
-
17
follower.account.send_change_notice act, reason[:set_card].name, reason[:option]
-
end
-
-
1
def act_followers act
-
86
@follower_stash ||= FollowerStash.new
-
86
act.actions(false).each do |a|
-
155
next if !a.card || a.card.silent_change?
-
-
154
@follower_stash.check_card a.card
-
end
-
86
@follower_stash
-
end
-
-
1
def silent_change
-
@silent_change || @supercard&.silent_change
-
end
-
-
1
def current_act_card?
-
222
return false unless act_card
-
-
222
act_card.id.nil? || act_card.id == id
-
# FIXME: currently card_id is nil for deleted acts (at least
-
# in the store phase when it's tested). The nil test was needed
-
# to make this work.
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/all/notify.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module Notify;
-
# Set: All cards (Notify, BaseViews)
-
#
-
1
module BaseViews;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/all/notify/base_views.rb"; end
-
2
module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
-
1
view :list_of_changes, denial: :blank, cache: :never do
-
48
action = notification_action voo.action_id
-
48
relevant_fields(action).map do |type|
-
118
edit_info_for(type, action)
-
end.compact.join
-
end
-
-
1
view :subedits, perms: :none, cache: :never do
-
34
return unless notification_act
-
-
34
wrap_subedits do
-
34
notification_act.actions_affecting(card).map do |action|
-
42
next if action.card_id == card.id
-
-
14
action.card.format(format: @format).render_subedit_notice action_id: action.id
-
end
-
end
-
end
-
-
1
view :subedit_notice, cache: :never do
-
14
action = notification_action voo.action_id
-
14
wrap_subedit_item do
-
14
%(#{name_before_action action} #{action.action_type}d\n) +
-
render_list_of_changes(action_id: action.id)
-
end
-
end
-
-
1
view :followed, perms: :none, compact: true do
-
34
if (set_card = followed_set_card) && (option_card = follow_option_card)
-
34
option_card.description set_card
-
else
-
"*followed set of cards*"
-
end
-
end
-
-
1
view :follower, perms: :none, compact: true do
-
active_notice(:follower) || "follower"
-
end
-
-
1
view :last_action_verb, cache: :never do
-
51
"#{notification_act&.main_action&.action_type || 'edite'}d"
-
end
-
-
1
view :unfollow_url, perms: :none, compact: true, cache: :never do
-
34
return "" unless (rule_name = live_follow_rule_name)
-
-
34
card_url path(mark: "#{active_notice(:follower)}+#{:follow.cardname}",
-
action: :update,
-
card: { subcards: { rule_name => Card[:never].name } })
-
end
-
-
1
def relevant_fields action
-
48
case action.action_type
-
22
when :create then %i[cardtype content]
-
24
when :update then %i[name cardtype content]
-
2
when :delete then %i[content]
-
end
-
end
-
-
1
def name_before_action action
-
14
(action.value(:name) && action.previous_value(:name)) || card.name
-
end
-
-
1
def followed_set_card
-
68
(set_name = active_notice(:followed_set)) && Card.fetch(set_name)
-
end
-
-
1
def follow_option_card
-
34
return unless (option_name = active_notice(:follow_option))
-
-
34
Card.fetch option_name
-
end
-
-
1
def active_notice key
-
170
@active_notice ||= inherit :active_notice
-
170
return unless @active_notice
-
-
170
@active_notice[key]
-
end
-
-
1
def live_follow_rule_name
-
34
return unless (set_card = followed_set_card) && (follower = active_notice(:follower))
-
-
34
set_card.follow_rule_name follower
-
end
-
-
1
def edit_info_for field, action
-
118
return nil unless (value = action.value field)
-
68
value = action.previous_value if action.action_type == :delete
-
68
wrap_list_item " #{notification_action_label action} #{field}: #{value}"
-
end
-
-
1
def notification_action_label action
-
68
case action.action_type
-
24
when :update then "new"
-
when :delete then "deleted"
-
end
-
end
-
-
1
def wrap_subedits
-
34
subedits = yield.compact.join
-
34
return "" if subedits.blank?
-
-
8
"\nThis update included the following changes:\n#{wrap_list subedits}"
-
end
-
-
1
def wrap_list list
-
4
"\n#{list}\n"
-
end
-
-
1
def wrap_list_item item
-
25
"#{item}\n"
-
end
-
-
1
def wrap_subedit_item
-
"\n#{yield}\n"
-
end
-
-
1
def notification_act act=nil
-
119
@notification_act ||= act || card.acts.last
-
end
-
-
1
def notification_action action_id
-
62
action_id ? Action.fetch(action_id) : card.last_action
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/all/notify/base_views.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module Notify;
-
# Set: All cards (Notify, HtmlViews)
-
#
-
1
module HtmlViews;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/all/notify/html_views.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
# view :last_action, perms: :none, cache: :never do
-
# _render_last_action_verb
-
# end
-
-
1
def wrap_list list
-
4
"<ul>#{list}</ul>\n"
-
end
-
-
1
def wrap_list_item item
-
43
"<li>#{item}</li>\n"
-
end
-
-
1
def wrap_subedit_item
-
14
"<li>#{yield}</li>\n"
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/all/notify/html_views.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Account" cards
-
#
-
1
module Account;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/right/account.rb"; end
-
1
def send_change_notice act, followed_set, follow_option
-
17
return unless email.present? && changes_visible?(act)
-
-
17
notify_of_act act do
-
17
{ follower: left.name, followed_set: followed_set, follow_option: follow_option }
-
end
-
end
-
-
1
def notify_of_act act
-
17
Auth.as(left.id) do
-
17
Card[:follower_notification_email].deliver(
-
act.card, { to: email }, auth: left, active_notice: yield
-
)
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/right/account.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Follow" cards
-
#
-
# The Right::Follow set configures follow preferences (`[Set]+[User]+:follow`)
-
# While the user follow dashboard ([User]+:follow`) is also in this Set, its
-
1
module Follow;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/right/follow.rb"; end
-
# customizations are handled in TypePlusRight::User::Follow
-
-
1
event :cache_expired_for_new_preference, :integrate, when: :is_preference? do
-
21
Card.follow_caches_expired
-
end
-
-
1
def option_cards
-
Card::FollowOption.cards.compact
-
end
-
-
1
def options_rule_card
-
Card.new(
-
name: "follow_options_card",
-
type_code: :pointer,
-
content: option_cards.map { |oc| "[[#{oc.name}]]" }.join("\n")
-
)
-
end
-
-
1
def add_follow_item? condition
-
new_card? || !include_item?(condition)
-
end
-
-
1
def ok_to_update
-
4
permit :update
-
end
-
-
1
def ok_to_create
-
20
permit :create
-
end
-
-
1
def ok_to_delete
-
1
permit :delete
-
end
-
-
1
def permit action, verb=nil
-
25
if %i[create delete update].include?(action) && allowed_to_change_follow_status?
-
25
true
-
else
-
super action, verb
-
end
-
end
-
-
1
def allowed_to_change_follow_status?
-
25
Auth.signed_in? &&
-
((user = rule_user) && Auth.current_id == user.id) || Auth.always_ok?
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
# shows a follow item link for each of the current follow options
-
1
view :follow_status, cache: :never do
-
wrap { haml :follow_status }
-
end
-
-
# interface to view/alter a specific rule option
-
1
view :follow_item, cache: :never do
-
follow_item Env.params[:condition]
-
end
-
-
1
def follow_item condition, button=true
-
condition ||= "*always"
-
wrap do
-
card_form action: :update, success: { view: :follow_item } do
-
[
-
follow_item_hidden_tags(condition),
-
(follow_item_button(condition) if button),
-
follow_item_link(condition)
-
].compact
-
end
-
end
-
end
-
-
1
def rule_form_args
-
super.merge "data-update-foreign-slot": ".card-slot.follow_section-view"
-
end
-
-
1
private
-
-
1
def follow_item_hidden_tags condition
-
condkey = card.add_follow_item?(condition) ? :add_item : :drop_item
-
hidden_tags condition: condition, condkey => condition
-
end
-
-
1
def follow_item_button condition
-
action = card.add_follow_item?(condition) ? :add : :delete
-
button_tag type: :submit, "aria-label": "Left Align",
-
class: "btn-sm btn-item #{follow_item_button_class action}" do
-
follow_item_icon action
-
end
-
end
-
-
1
def follow_item_button_class action
-
action == :add ? "btn-item-add" : "btn-item-delete btn-primary"
-
end
-
-
1
def follow_item_icon action
-
icon_tag(action == :add ? :add : :check)
-
end
-
-
1
def follow_item_link condition
-
link_to_card follow_item_link_target, follow_item_link_text(condition)
-
end
-
-
1
def follow_item_link_target
-
set = card.rule_set
-
setname = set.name
-
set.tag.codename == :self ? setname.left : setname.field("by name")
-
end
-
-
1
def follow_item_link_text condition
-
if (option_card = Card.fetch condition)
-
option_card.description card.rule_set
-
else
-
card.rule_set.follow_label
-
end
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/right/follow.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+FollowFields" cards
-
#
-
1
module FollowFields;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/right/follow_fields.rb"; end
-
1
event :follow_fields_changed, :integrate do
-
Card.follow_caches_expired
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/right/follow_fields.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Followers" cards
-
#
-
# -*- encoding : utf-8 -*-
-
-
1
module Followers;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/right/followers.rb"; end
-
# X+*followers provides a list of all users following X.
-
-
1
include_set Abstract::Pointer
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :core, cache: :never do
-
4
super()
-
end
-
end
-
-
1
def content
-
left ? item_names.to_pointer_content : ""
-
end
-
-
1
def item_names _args={}
-
8
left ? left.follow_set_card.prototype.follower_names : []
-
end
-
-
1
def virtual?
-
24
new?
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/right/followers.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Following" cards
-
#
-
1
module Following;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/right/following.rb"; end
-
1
def virtual?
-
new?
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
if card.left && Auth.signed_in?
-
render_rule_editor
-
else
-
nest Card.fetch(card.name.left, :followers), view: :titled, items: { view: :link }
-
end
-
end
-
-
1
view :status do
-
if (rcard = current_follow_rule_card)
-
rcard.item_cards.map do |item|
-
%(<div class="alert alert-success" role="alert">
-
<strong>#{rcard.rule_set.follow_label}</strong>: #{item.title}
-
</div>)
-
end.join
-
else
-
"No following preference"
-
end
-
end
-
-
1
view :one_line_content do
-
""
-
end
-
-
1
view :rule_editor, cache: :never do
-
rule_context = Card.fetch preference_name, new: { type_id: PointerID }
-
wrap_with :div, class: "edit-rule" do
-
follow_context = current_follow_rule_card || rule_context
-
subformat(follow_context).rule_form :open, rule_context, :modal
-
end
-
end
-
-
1
def preference_name
-
set_name = card.left.follow_set_card.name
-
Card::Name[set_name, Auth.current.name, :follow]
-
end
-
-
1
def edit_rule_success
-
{ view: "status", id: card.name.url_key }
-
end
-
-
1
def current_follow_rule_card
-
card.left.preference :follow
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/right/following.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Always"
-
#
-
1
module Always;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/self/always.rb"; end
-
1
include_set Abstract::FollowOption
-
-
1
follow_opts position: 2
-
-
29
follow_test { |_follower_id, _accounted_ids| true }
-
-
1
def title
-
"Following"
-
end
-
-
1
def label
-
"follow"
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/self/always.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Created"
-
#
-
1
module Created;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/self/created.rb"; end
-
1
include_set Abstract::FollowOption
-
-
1
restrictive_follow_opts position: 1
-
-
1
follower_candidate_ids do |card|
-
335
[card.creator_id]
-
end
-
-
1
def title
-
"Following content you created"
-
end
-
-
1
def label
-
"follow if I created"
-
end
-
-
1
def description set_card
-
"#{set_card.follow_label} I created"
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/self/created.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Edited"
-
#
-
1
module Edited;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/self/edited.rb"; end
-
1
include_set Abstract::FollowOption
-
-
1
restrictive_follow_opts position: 2
-
-
1
follower_candidate_ids do |card|
-
# FIXME? - could optimize by not using cards table...
-
335
card.id ? Card.search(editor_of: card.id, return: :id) : []
-
end
-
-
1
def title
-
"Following content you edited"
-
end
-
-
1
def label
-
"follow if I edited"
-
end
-
-
1
def description set_card
-
"#{set_card.follow_label} I edited"
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/self/edited.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Follow"
-
#
-
1
module Follow;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/self/follow.rb"; end
-
1
extend Card::Setting
-
1
setting_opts group: :other, position: 7, rule_type_editable: false, user_specific: true
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/self/follow.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "FollowDefaults"
-
#
-
# DEPRECATED
-
#
-
# Despite its name (*follow defaults)card does not influence defaults for *follow rules.
-
# What it does is provide a mechanism (with interface) for updating all users so that
-
# they follow the items that are its content.
-
#
-
# PLAN:
-
1
module FollowDefaults;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/self/follow_defaults.rb"; end
-
# - actual defaults should be handled as much as possible with something like
-
# the *defaults rule
-
# - on the *admin page, we can have a link so sharks can update all the pristine cards
-
# to use whatever the actual defaults representation is (see previous point)
-
# - if you truly want to override existing follow rules, that may be monkey territory?
-
# - we will delete "*follow defaults" after the above are completed
-
-
1
event :update_follow_rules, :finalize, on: :save, when: :update_all_users do
-
Auth.as_bot do
-
Card.search(type: "user").each do |user|
-
follow_defaults.each do |set_card, option|
-
follow_rule = Card.fetch(set_card.follow_rule_name(user.name), new: {})
-
next unless follow_rule
-
-
follow_rule.drop_item "*never"
-
follow_rule.drop_item "*always"
-
follow_rule.add_item option
-
follow_rule.save!
-
end
-
end
-
end
-
Card.follow_caches_expired
-
end
-
-
1
def follow_defaults
-
item_names.map do |item|
-
if (set_card = Card.fetch item.to_name.left)&.type_code == :set
-
[set_card, follow_option(item)]
-
end
-
end.compact
-
end
-
-
1
def follow_option item
-
option_card =
-
Card.fetch(item.to_name.right) || Card[item.to_name.right.to_sym]
-
option_card.follow_option? ? option_card.name : "*always"
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :edit, perms: :update, unknown: true do
-
frame_and_form :update, hidden: { success: "_self",
-
card: { update_all_users: false } } do
-
[
-
_render_content_formgroups,
-
_render_confirm_update_all,
-
_render_edit_buttons
-
]
-
end
-
end
-
-
1
view :edit_buttons do
-
button_formgroup do
-
[submit_and_update_button, simple_submit_button, cancel_to_edit_button]
-
end
-
end
-
-
1
def submit_and_update_button
-
submit_button text: "Submit and update all users",
-
disable_with: "Updating", class: "follow-updater"
-
end
-
-
1
def simple_submit_button
-
button_tag "Submit", class: "follow"
-
end
-
-
1
def cancel_to_edit_button
-
cancel_button href: path(view: :edit, id: card.id)
-
end
-
-
1
view :confirm_update_all do
-
wrap do
-
alert "info" do
-
%(
-
<h1>Are you sure you want to change the default follow rules?</h1>
-
<p>You may choose to update all existing users.
-
This may take a while. </p>
-
)
-
end
-
end
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/self/follow_defaults.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "Never"
-
#
-
1
module Never;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/self/never.rb"; end
-
1
include_set Abstract::FollowOption
-
-
1
follow_opts position: 3
-
-
3
follow_test { |_follower_id, _accounted_ids| false }
-
-
1
def title
-
"Ignoring"
-
end
-
-
1
def label
-
"ignore"
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/self/never.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Cardtype" cards
-
#
-
1
module Cardtype;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/type/cardtype.rb"; end
-
1
def follow_label
-
follow_set_card.follow_label
-
end
-
-
1
def followed_by? user_id=nil
-
follow_set_card.all_members_followed_by? user_id
-
end
-
-
1
def follow_set_card
-
Card.fetch name, :type
-
end
-
-
1
def list_direct_followers?
-
true
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def related_by_type_items
-
super.unshift ["#{card.name} cards", [card, :type, :by_name], mark: :absolute]
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/type/cardtype.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Set" cards
-
#
-
1
module Set;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/type/set.rb"; end
-
1
event :cache_expired_for_new_set, :store, on: :create do
-
20
Card.follow_caches_expired
-
end
-
-
1
def list_direct_followers?
-
true
-
end
-
-
1
def follow_label
-
36
if (klass = subclass_for_set)
-
36
klass.short_label name.left_name
-
else
-
""
-
end
-
end
-
-
1
def follow_rule_name user=nil
-
34
Card::Name[[name, user, :follow].compact]
-
end
-
-
1
def followed_by? user_id=nil
-
all_members_followed_by? user_id
-
end
-
-
1
def follow_set_card
-
4
self
-
end
-
-
1
def all_members_followed?
-
all_members_followed_by? Auth.current_id
-
end
-
-
1
def all_members_followed_by? user_id=nil
-
return false unless prototype.followed_by?(user_id)
-
-
directly_followed_by?(user_id) || broader_set_followed_by?(user_id)
-
end
-
-
1
def broader_set_followed_by? user_id
-
broader_sets.find do |set_name|
-
Card.fetch(set_name)&.directly_followed_by? user_id
-
end
-
end
-
-
1
def directly_followed?
-
directly_followed_by? Auth.current_id
-
end
-
-
1
def directly_followed_by? user_id=nil
-
return true if user_id && follow_rule?(user_id)
-
-
follow_rule?
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/type/set.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "User" cards
-
#
-
1
module User;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/type/user.rb"; end
-
1
def follow follow_name, option="*always"
-
return unless (follow_rule = Card.fetch(follow_name)&.follow_rule_card(name, new: {}))
-
-
follow_rule.drop_item "*never"
-
follow_rule.add_item option
-
follow_rule.save!
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/type/user.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class TypePlusRight; module User;
-
# Set: All "+Follow" cards on "User" cards
-
#
-
1
module Follow;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/type_plus_right/user/follow.rb"; end
-
1
FOLLOW_TABS = { "Follow" => "follow_tab", "Ignore" => "ignore_tab" }.freeze
-
-
# a virtual pointer to the sets that a user is following.
-
# (data is stored in preferences: `[Set]+[User]+:follow`)
-
-
1
include_set Abstract::Pointer
-
1
def virtual?
-
new?
-
end
-
-
# overrides pointer default
-
1
def item_names _args={}
-
4
if (user = left)
-
4
Card::Rule.preference_names user.name, "follow"
-
else
-
[]
-
end
-
end
-
-
1
def suggestions
-
Card[:follow_suggestions]&.item_names || []
-
end
-
-
1
def current_user?
-
Auth.signed_in? && Auth.current_id == left.id
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :one_line_content do
-
""
-
end
-
-
1
view :edit do
-
render :open
-
end
-
-
# renders follow tab and ignore tab
-
1
view :core do
-
2
tabs FOLLOW_TABS, "follow_tab", load: :lazy do
-
render_follow_tab
-
end
-
end
-
-
1
view :follow_tab, cache: :never do
-
haml :follow_editor, items_method: :following_rules_and_options
-
end
-
-
1
view :ignore_tab, cache: :never do
-
haml :follow_editor, items_method: :ignoring_rules_and_options
-
end
-
-
1
def show_button?
-
card.current_user? || Auth.always_ok?
-
end
-
-
1
def pointer_items args
-
voo.items[:view] ||= :link
-
super(args)
-
end
-
-
# TODO: research and generalize
-
# this does not look specific to following!
-
1
view :errors, perms: :none do
-
return unless card.errors.any?
-
-
if card.errors.find { |attrib, _msg| attrib == :permission_denied }
-
Env.save_interrupted_action(request.env["REQUEST_URI"])
-
voo.title = "Problems with #{card.name}"
-
class_up "d0-card-frame", "card card-warning card-inverse"
-
frame do
-
"Please #{link_to_card :signin, 'sign in'}" # " #{to_task}"
-
end
-
else
-
super()
-
end
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/type_plus_right/user/follow.rb ~~
-
# -*- encoding : utf-8 -*-
-
5
class Card; module Set; class TypePlusRight; module User;; module Follow;
-
# Set: All "+FollowEditorHelper" cards on "User+Follow" cards (FollowEditorHelper)
-
#
-
# all the following methods are used to construct the Follow and Ignore tabs
-
-
1
module FollowEditorHelper;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/type_plus_right/user/follow/follow_editor_helper.rb"; end
-
# TODO: these object representations are complex enough for their own class
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
# constructs hash of rules/options for "Follow" tab
-
1
def following_rules_and_options &block
-
rule_opt_array = following_rule_options_hash.map do |key, val|
-
[(Card.fetch key, new: {}), val]
-
end
-
rules_and_options_by_set_pattern Hash[rule_opt_array], &block
-
end
-
-
# constructs hash of rules/options for "Ignore" tab
-
1
def ignoring_rules_and_options &block
-
rule_opts_hash = ignore_rules.each_with_object({}) do |rule, hash|
-
hash[rule] = [:never.cardname]
-
end
-
rules_and_options_by_set_pattern rule_opts_hash, &block
-
end
-
-
1
private
-
-
# all rules with ignore
-
1
def ignore_rules
-
never = :never.cardname.key
-
card.item_cards.select do |follow_rule|
-
follow_rule.item_names.select { |n| n.key == never }.any?
-
end
-
end
-
-
# @param rule_opts_hash [Hash] { rule1_card => rule1_follow_options }
-
# for each rule/option variant, yields with rule_card and option params
-
1
def rules_and_options_by_set_pattern rule_opts_hash
-
pattern_hash = a_set_pattern_hash rule_opts_hash
-
empty = true
-
Card.set_patterns.reverse.map do |pattern|
-
pattern_hash[pattern].each do |rule_card, options|
-
options.each do |option|
-
yield rule_card, option
-
empty = false
-
end
-
end
-
end
-
yield nil if empty
-
end
-
-
1
def a_set_pattern_hash rule_opts_hash
-
pattern_hash = Hash.new { |h, k| h[k] = [] }
-
rule_opts_hash.each do |rule_card, options|
-
pattern_hash[rule_card.rule_set.subclass_for_set] << [rule_card, options]
-
end
-
pattern_hash
-
end
-
-
# @return Hash # { rule1 => rule1_follow_options }
-
1
def following_rule_options_hash
-
merge_option_hashes current_following_rule_options_hash,
-
suggested_following_rule_options_hash
-
end
-
-
# adds suggested follow options to existing rules where applicable
-
1
def merge_option_hashes current, suggested
-
current.each do |key, current_opt|
-
if (suggested_opt = suggested.delete(key))
-
current[key] = (current_opt + suggested_opt).uniq
-
end
-
end
-
current.merge suggested
-
end
-
-
# @return Hash # { existing_rule1 => rule1_follow_options } (excluding never)
-
# (*never is excluded because this list is for the Follow tab, and *never is
-
# handled under the Ignore tab)
-
1
def current_following_rule_options_hash
-
never = :never.cardname
-
card.item_cards.each_with_object({}) do |follow_rule, hash|
-
hash[follow_rule.key] = follow_rule.item_names.reject { |item| item == never }
-
end
-
end
-
-
# @return Hash # { suggested_rule1 => rule1_follow_options }
-
1
def suggested_following_rule_options_hash
-
return {} unless card.current_user?
-
-
card.suggestions.each_with_object({}) do |sug, hash|
-
set_card, opt = a_set_and_option_suggestion(sug) || a_set_only_suggestion(sug)
-
hash[set_card.follow_rule_name(card.trunk).key] = [opt]
-
end
-
end
-
-
# @param sug [String] follow suggestion
-
# @return [Array] set_card and option
-
# suggestion value contains both set and follow option
-
1
def a_set_and_option_suggestion sug
-
return unless (set_card = valid_set_card(sug.to_name.left))
-
-
[set_card, suggested_follow_option(sug.to_name.right)]
-
end
-
-
1
def suggested_follow_option name
-
# FIXME: option should be unambiguously name or codename
-
# (if codename use colon or Symbol)
-
option_card = Card.fetch(name) || Card[name.to_sym]
-
option_card&.follow_option? ? option_card.name : :always.cardname
-
end
-
-
# @param sug [String] follow suggestion
-
# @return [Array] set_card and option
-
# suggestion value contains only set (implies *always)
-
1
def a_set_only_suggestion sug
-
return unless (set_card = valid_set_card(sug))
-
-
yield set_card, :always.cardname
-
end
-
-
1
def valid_set_card name
-
card = Card.fetch(name)
-
card&.type_code == :set ? card : false
-
end
-
end
-
end;end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/type_plus_right/user/follow/follow_editor_helper.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (GoogleAnalytics)
-
#
-
1
module GoogleAnalytics;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-google_analytics/set/all/google_analytics.rb"; end
-
1
require "staccato"
-
-
1
mattr_accessor :server_side_tracking_formats
-
-
1
self.server_side_tracking_formats = %i[csv json]
-
-
1
event :track_page, before: :show_page, when: :track_page? do
-
tracker.pageview path: Env.controller.request&.path, host: Env.host, title: name
-
end
-
-
1
def track_page?
-
315
google_analytics_key &&
-
Env.controller&.response_format&.in?(server_side_tracking_formats)
-
end
-
-
1
def tracker
-
return unless google_analytics_key
-
-
::Staccato.tracker google_analytics_key # , nil, ssl: true
-
end
-
-
1
def google_analytics_key
-
539
@google_analytics_key ||=
-
Card::Rule.global_setting(:google_analytics_key) ||
-
Card.config.google_analytics_key
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
delegate :tracker, :google_analytics_key, to: :card
-
-
1
def views_in_head
-
224
super << :google_analytics_snippet
-
end
-
-
1
view :google_analytics_snippet, unknown: true, perms: :none do
-
224
haml :google_analytics_snippet if google_analytics_key
-
end
-
-
1
def google_analytics_snippet_vars
-
{ anonymizeIp: true }
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-google_analytics/set/all/google_analytics.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Markdown" cards
-
#
-
1
module Markdown;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-markdown/set/type/markdown.rb"; end
-
1
require "kramdown"
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
50
safe_process_content do |content|
-
50
Kramdown::Document.new(content).to_html
-
end
-
end
-
-
1
def input_type
-
:ace_editor
-
end
-
-
1
def ace_mode
-
:markdown
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-markdown/set/type/markdown.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (ProsemirrorEditor)
-
#
-
1
module ProsemirrorEditor;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-prosemirror_editor/set/all/prosemirror_editor.rb"; end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def prosemirror_editor_input
-
wrap_with :div, id: unique_id, class: "prosemirror-editor" do
-
hidden_field :content, class: "d0-card-content", value: card.content
-
end
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-prosemirror_editor/set/all/prosemirror_editor.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "ScriptProsemirror"
-
#
-
1
module ScriptProsemirror;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-prosemirror_editor/set/self/script_prosemirror.rb"; end
-
1
include_set Abstract::CodeFile
-
-
1
Self::ScriptMods.add_item :script_prosemirror
-
1
Self::InputOptions.add_to_basket :options, "prosemirror editor"
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-prosemirror_editor/set/self/script_prosemirror.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "ScriptProsemirrorConfig"
-
#
-
1
module ScriptProsemirrorConfig;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-prosemirror_editor/set/self/script_prosemirror_config.rb"; end
-
1
include_set Abstract::CodeFile
-
-
1
Self::ScriptEditors.add_item :script_prosemirror_config
-
1
All::Head::HtmlFormat.add_to_basket :mod_js_config,
-
[:prose_mirror, "setProseMirrorConfig"]
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-prosemirror_editor/set/self/script_prosemirror_config.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "StyleProsemirror"
-
#
-
1
module StyleProsemirror;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-prosemirror_editor/set/self/style_prosemirror.rb"; end
-
1
include_set Abstract::CodeFile
-
-
1
Self::StyleMods.add_item :style_prosemirror
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-prosemirror_editor/set/self/style_prosemirror.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (Recaptcha)
-
#
-
1
module Recaptcha;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-recaptcha/set/all/recaptcha.rb"; end
-
RECAPTCHA_ERROR_CODES = { # LOCALIZE
-
1
"missing-input-secret" => "secret parameter is missing",
-
"invalid-input-secret" => "secret parameter is invalid or malformed",
-
"missing-input-response" => "response parameter is missing",
-
"invalid-input-response" => "response parameter is invalid or malformed",
-
"bad-request" => "request is invalid or malformed"
-
}
-
-
1
def human?
-
result = JSON.parse recaptcha_response
-
return if recaptcha_success?(result)
-
-
add_recaptcha_errors result["error-codes"]
-
end
-
-
1
def recaptcha_on?
-
235
recaptcha_keys? &&
-
Env[:controller] &&
-
!Auth.signed_in? &&
-
!Auth.always_ok? &&
-
!Auth.needs_setup? &&
-
Card::Rule.toggle(rule(:captcha))
-
end
-
-
1
def add_recaptcha_errors error_codes
-
if error_codes.present?
-
error_codes.each do |code|
-
errors.add :recaptcha, RECAPTCHA_ERROR_CODES.fetch(code, code)
-
end
-
else
-
errors.add :recaptcha, "Looks like you are not a human" # LOCALIZE
-
end
-
end
-
-
1
def recaptcha_success? result
-
result['success'] &&
-
(result['score'].to_f >= Cardio.config.recaptcha_minimum_score) &&
-
(result['action'].to_sym == action.to_sym)
-
end
-
-
1
def recaptcha_response
-
::Recaptcha.get({ secret: Card.config.recaptcha_secret_key,
-
response: Env.params[:recaptcha_token] }, {})
-
end
-
-
1
def recaptcha_keys?
-
235
Card.config.recaptcha_site_key && Card.config.recaptcha_secret_key
-
end
-
-
1
event :recaptcha, :validate, when: :validate_recaptcha? do
-
handle_recaptcha_config_errors do
-
Env[:recaptcha_used] = true
-
human?
-
end
-
end
-
-
1
def handle_recaptcha_config_errors
-
if Env.params[:recaptcha_token] == "grecaptcha-undefined"
-
errors.add "recaptcha", "needs correct v3 configuration" # LOCALILZE
-
elsif Env.params[:recaptcha_token] == "recaptcha-token-field-missing"
-
raise Card::Error, "recaptcha token field missing" # LOCALILZE
-
else
-
yield
-
end
-
end
-
-
-
1
def validate_recaptcha?
-
219
!@supercard && !Env[:recaptcha_used] && recaptcha_on?
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def recaptcha_token action
-
output [
-
javascript_include_tag(recaptcha_script_url),
-
hidden_field_tag("recaptcha_token", "",
-
"data-site-key": Card.config.recaptcha_site_key,
-
"data-action": action,
-
class: "_recaptcha-token")
-
]
-
end
-
-
1
def recaptcha_script_url
-
"https://www.google.com/recaptcha/api.js?render=#{Card.config.recaptcha_site_key}"
-
end
-
-
1
def hidden_form_tags action, opts
-
341
return super unless recaptcha?(opts)
-
-
super + recaptcha_token(action)
-
end
-
-
1
def card_form_html_opts action, opts={}
-
341
super
-
341
opts["data-recaptcha"] ||= "on" if recaptcha?(opts)
-
341
opts
-
end
-
-
1
def recaptcha? opts
-
682
card.recaptcha_on? && opts[:recaptcha] != :off
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-recaptcha/set/all/recaptcha.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "AdminInfo"
-
#
-
1
module AdminInfo;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-recaptcha/set/self/admin_info.rb"; end
-
1
add_to_basket :warnings, :recaptcha_config_issues
-
-
1
def recaptcha_config_issues?
-
RecaptchaCard.using_defaults?
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def recaptcha_config_issues_message
-
warning =
-
if Card::Env.localhost?
-
# %(Your captcha is currently working with temporary settings.
-
# This is fine for a local installation, but you will need new
-
# recaptcha keys if you want to make this site public.)
-
I18n.t(:captcha_temp, scope: "mod.admin.set.self.admin_info",
-
recaptcha_link: add_recaptcha_keys_link)
-
else
-
# %(You are configured to use [[*captcha]], but for that to work
-
# you need new recaptcha keys.)
-
I18n.t(:captcha_keys, scope: "mod.admin.set.self.admin_info",
-
recaptcha_link: add_recaptcha_keys_link,
-
captcha_link: link_to_card(:captcha))
-
end
-
<<-HTML
-
<p>#{warning}</p>
-
HTML
-
end
-
-
1
def add_recaptcha_keys_link
-
link_text = I18n.t :recaptcha_keys, scope: "mod.admin.set.self.admin_info"
-
Card[:recaptcha_settings]&.format&.edit_link link_text: link_text
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-recaptcha/set/self/admin_info.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "RecaptchaProxy"
-
#
-
1
module RecaptchaProxy;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-recaptcha/set/self/recaptcha_proxy.rb"; end
-
1
event :set_recaptcha_proxy, :finalize do
-
Card.config.recaptcha_proxy = content
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-recaptcha/set/self/recaptcha_proxy.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "RecaptchaSecretKey"
-
#
-
1
module RecaptchaSecretKey;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-recaptcha/set/self/recaptcha_secret_key.rb"; end
-
1
event :validate_recaptcha_secret_key, :validate do
-
return if content.match?(/^[a-zA-Z0-9\-_]*$/)
-
-
errors.add :content, "invalid key" # LOCALIZE
-
end
-
-
1
event :set_recaptcha_secret_key, :finalize do
-
Card.config.recaptcha_secret_key = content
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-recaptcha/set/self/recaptcha_secret_key.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "RecaptchaSettings"
-
#
-
1
module RecaptchaSettings;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-recaptcha/set/self/recaptcha_settings.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def raw_help_text
-
# LOCALIZE
-
"Register your domain at Google's [[http://google.com/recaptcha|reCAPTCHA service]] "\
-
"and enter your site key and secret key below.<br>"\
-
"If you want to turn catchas off then change all [[*captcha|captcha rules]] to 'no'."
-
end
-
-
# def instructions title, steps
-
# steps = list_tag steps, ordered: true
-
# "#{title}#{steps}"
-
# end
-
#
-
# <h5>#{instructions}</h5>
-
# #{howto_add_new_recaptcha_keys}
-
# #{howto_turn_captcha_off}
-
#
-
# def howto_add_new_recaptcha_keys
-
# instructions(
-
# I18n.t(:howto_add_keys, scope: "mod.admin.set.self.admin_info"),
-
# [
-
# I18n.t(:howto_register,
-
# scope: "mod.admin.set.self.admin_info",
-
# recaptcha_link: link_to_resource("http://google.com/recaptcha")),
-
# I18n.t(:howto_add,
-
# scope: "mod.admin.set.self.admin_info",
-
# recaptcha_settings: link_to_card(:recaptcha_settings))
-
# ]
-
# )
-
# end
-
#
-
# def howto_turn_captcha_off
-
# instructions(
-
# I18n.t(:howto_turn_off, scope: "mod.admin.set.self.admin_info"),
-
# [
-
# I18n.t(:howto_go,
-
# scope: "mod.admin.set.self.admin_info",
-
# captcha_card: link_to_card(:captcha)),
-
# I18n.t(:howto_update,
-
# scope: "mod.admin.set.self.admin_info")
-
# ]
-
# )
-
# end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-recaptcha/set/self/recaptcha_settings.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "RecaptchaSiteKey"
-
#
-
1
module RecaptchaSiteKey;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-recaptcha/set/self/recaptcha_site_key.rb"; end
-
1
event :validate_recaptcha_site_key, :validate do
-
return if content.match?(/^[a-zA-Z0-9\-_]*$/)
-
-
errors.add :content, "invalid key" # LOCALIZE
-
end
-
-
1
event :set_recaptcha_site_key, :finalize do
-
Card.config.recaptcha_site_key = content
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-recaptcha/set/self/recaptcha_site_key.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Self" cards
-
#
-
1
module Self;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/right/self.rb"; end
-
1
def prototype_default_card
-
left
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/right/self.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Rstar
-
# Set: All "+*" cards (RuleUser)
-
#
-
1
module RuleUser;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rstar/rule_user.rb"; end
-
-
1
def rule_user_name
-
is_preference? ? name.trunk_name.tag : nil
-
end
-
-
1
def rule_user
-
25
is_preference? ? self[-2] : nil
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rstar/rule_user.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Rule
-
# Set: All rule cards (BarView)
-
#
-
1
module BarView;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rule/bar_view.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
bar_cols 6, 6
-
1
info_bar_cols 5, 4, 3
-
-
1
def existing_rule_card
-
4
@existing_rule_card ||= find_existing_rule_card
-
end
-
-
1
view :bar, unknown: true do
-
1
voo.hide :bar_nav unless existing_rule_card
-
1
super()
-
end
-
-
1
view :expanded_bar, unknown: true do
-
super()
-
end
-
-
1
view :one_line_content,
-
wrap: { div: { class: "text-muted one-line" } }, unknown: true do
-
1
return render_mini_unknown unless existing_rule_card
-
-
1
with_nest_mode :compact do
-
1
one_line_content
-
end
-
end
-
-
1
view :raw_one_line_content,
-
wrap: { div: { class: "text-muted one-line" } }, unknown: true do
-
return render_mini_unknown unless existing_rule_card
-
-
raw_one_line_content
-
end
-
-
1
view :bar_bottom, unknown: true do
-
if nest_mode == :edit
-
current_rule_form
-
else
-
nest existing_rule_card, view: :core
-
end
-
end
-
-
1
view :bar_middle, unknown: true do
-
rule_info
-
end
-
-
1
view :bar_left, unknown: true do
-
1
super()
-
end
-
-
1
view :bar_right, unknown: true do
-
1
voo.show?(:bar_bottom) ? rule_info : rule_short_content
-
end
-
-
1
def rule_short_content
-
1
return "" unless existing_rule_card
-
-
1
nest existing_rule_card, { view: :one_line_content },
-
set_context: card.name.trunk_name
-
end
-
-
1
def bar_title
-
1
return super() if voo.show? :full_name
-
-
linking_to_existing_rule { card.rule_setting_title }
-
end
-
-
# LOCALIZE
-
1
def rule_info
-
return wrap_with(:em, "no existing #{setting_link} rule") unless existing_rule_card
-
-
wrap_with :span,
-
"#{rule_setting_link} rule that applies to "\
-
"#{rule_set_link existing_rule_card}"
-
end
-
-
1
def rule_setting_link
-
link_to_card card.rule_setting, card.rule_setting_name
-
end
-
-
1
def rule_set_link existing_rule
-
count = link_to_card [card.rule_set, :by_name], card.rule_set.count
-
"#{link_to_card card.rule_set, existing_rule.trunk&.label&.downcase} (#{count})"
-
end
-
-
1
private
-
-
1
def linking_to_existing_rule
-
return yield unless existing_rule_card && voo.show?(:toggle)
-
-
link_to_view bar_title_toggle_view, yield
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rule/bar_view.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Rule
-
# Set: All rule cards (BridgeRulesEditor)
-
#
-
1
module BridgeRulesEditor;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rule/bridge_rules_editor.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :overlay_rule, cache: :never, unknown: true do
-
wrap_with_overlay slot: breadcrumb_data("Rule editing", "rules") do
-
current_rule_form
-
end
-
end
-
-
1
view :modal_rule, cache: :never, unknown: true,
-
wrap: { modal: { title: ->(format) { format.render_title } } } do
-
current_rule_form
-
end
-
-
1
view :overlay_title do
-
edit_rule_title
-
end
-
-
1
view :help_text, unknown: true, cache: :never do
-
19
wrap_help_text [rule_based_help, setting_link].join(" ")
-
end
-
-
1
def setting_link
-
19
wrap_with :div, class: "ml-auto" do
-
19
link_to_card card.rule_setting_name,
-
" (#{card.rule_setting.count} #{card.rule_setting_title} rules)",
-
class: "text-muted"
-
end
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rule/bridge_rules_editor.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Rule
-
# Set: All rule cards (Editor)
-
#
-
1
module Editor;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rule/editor.rb"; end
-
1
def left_type_for_nest_editor_set_selection
-
return super unless is_template?
-
-
case Card.fetch_id rule_set_pattern_name
-
when TypeID
-
rule_set.anchor_name
-
when SelfID
-
rule_set.anchor.type_name
-
else
-
super
-
end
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
attr_accessor :rule_context
-
-
1
view :rule_edit, cache: :never, unknown: true,
-
wrap: { modal: { size: :large,
-
title: :edit_rule_title,
-
footer: "" } } do
-
current_rule_form form_type: :modal
-
end
-
-
1
view :rule_help, unknown: true, perms: :none, cache: :never do
-
wrap_with :div, class: "alert alert-info rule-instruction" do
-
rule_based_help
-
end
-
end
-
-
1
view :rule_bridge_link, unknown: true do
-
opts = bridge_link_opts(class: "edit-rule-link nav-link",
-
"data-toggle": "pill",
-
"data-cy": "#{setting_title.to_name.key}-pill")
-
opts[:path].delete(:layout)
-
link_to_view(:overlay_rule, (setting_title + short_help_text), opts)
-
end
-
-
1
def edit_link_view
-
2
:rule_edit
-
end
-
-
1
def edit_rule_title
-
output [
-
wrap_with(:h5, setting_title, class: "title font-weight-bold")
-
# render_overlay_rule_help
-
]
-
end
-
-
1
def current_rule
-
if params[:assign]
-
card
-
elsif (existing = find_existing_rule_card)
-
existing
-
else
-
card
-
end
-
end
-
-
1
def quick_editor
-
rule_content_formgroup
-
end
-
-
1
def setting_title
-
card.name.tag.tr "*", ""
-
end
-
-
1
def short_help_text
-
"<div class=\"help-text\">#{card.short_help_text}</div>"
-
end
-
-
1
def rule_set_description
-
2
card.rule_set.follow_label
-
end
-
-
1
def rules_type_formgroup
-
return unless card.right.rule_type_editable
-
-
success = @edit_rule_success
-
wrap_type_formgroup do
-
type_field(
-
href: path(mark: success[:id], view: :rule_form, assign: true),
-
class: "type-field rule-type-field live-type-field",
-
"data-remote" => true
-
)
-
end
-
end
-
-
1
def rule_content_formgroup
-
_render_content_formgroup hide: :conflict_tracker
-
end
-
-
1
def current_set_key
-
card.new_card? ? Card.quick_fetch(:all).name.key : card.rule_set_key
-
end
-
-
1
private
-
-
1
def find_existing_rule_card
-
2
card.new_card? ? existing_rule_from_prototype : card
-
end
-
-
# self.card is a POTENTIAL rule; it quacks like a rule but may or may not exist.
-
# This generates a prototypical member of the POTENTIAL rule's set
-
# and returns that member's ACTUAL rule for the POTENTIAL rule's setting
-
1
def existing_rule_from_prototype
-
return unless (setting = card.right)
-
-
card.set_prototype.rule_card setting.codename, user: card.rule_user
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rule/editor.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Rule
-
# Set: All rule cards (HtmlViews)
-
#
-
1
module HtmlViews;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rule/html_views.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
# Rule cards that are searches are usual right structures and refer to the left
-
# in the search query. In that case the search doesn't work
-
# properly in the context of the rule card itself. Hence we show the query syntax
-
# and not the search result.
-
69
if card.type_id == SearchTypeID
-
render_raw
-
else
-
69
super()
-
end
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rule/html_views.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Rule
-
# Set: All rule cards (QuickEditor)
-
#
-
1
module QuickEditor;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rule/quick_editor.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :quick_edit, unknown: true, wrap: :slot do
-
quick_edit
-
end
-
-
1
view :quick_edit_success do
-
set_info true
-
end
-
-
1
def quick_edit
-
haml :quick_edit
-
end
-
-
1
def quick_form
-
card_form :update, quick_form_opts do
-
quick_editor
-
end
-
end
-
-
1
def quick_form_opts
-
{ "data-slot-selector": ".set-info.card-slot",
-
success: { view: :quick_edit_success } }
-
end
-
-
1
def set_info notify_change=nil
-
wrap true, class: "set-info" do
-
haml :set_info, notify_change: notify_change
-
end
-
end
-
-
1
def undo_button
-
link_to "undo", method: :post, rel: "nofollow", remote: true,
-
class: "btn btn-secondary ml-2 btn-sm btn-reduced-padding slotter",
-
"data-slot-selector": ".card-slot.quick_edit-view",
-
path: { action: :update,
-
revert_actions: [card.last_action_id],
-
revert_to: :previous }
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rule/quick_editor.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Rule
-
# Set: All rule cards (RuleForm)
-
#
-
1
module RuleForm;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rule_form.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :rule_form, cache: :never, unknown: true do
-
@success_view ||= :open
-
@rule_context ||= card
-
@form_type ||= :overlay
-
-
wrap do
-
edit_rule_form @success_view do
-
[
-
hidden_tags(success: @edit_rule_success),
-
haml(:rule_form)
-
].join
-
end
-
end
-
end
-
-
1
def form_type
-
@form_type || :overlay
-
end
-
-
1
def current_rule_form success_view: :overlay_rule, form_type: :overlay
-
current_rule_format = subformat current_rule
-
current_rule_format.rule_form success_view, card, form_type
-
end
-
-
1
def rule_form success_view, rule_context, form_type=:overlay
-
validate_form_type form_type
-
-
@rule_context = rule_context
-
@form_type = form_type
-
@success_view = success_view
-
-
render_rule_form
-
end
-
-
1
def validate_form_type form_type
-
return if form_type.in? %i[overlay modal]
-
-
raise "invalid rule_form type: #{form_type}; has to be overlay or modal"
-
end
-
-
1
def edit_rule_form success_view, &block
-
@rule_context ||= card
-
@edit_rule_success = edit_rule_success(success_view)
-
action_args = { action: :update, no_mark: true }
-
card_form action_args, rule_form_args, &block
-
end
-
-
1
def rule_form_args
-
{ class: "card-rule-form", "data-slotter-mode": "update-origin" }
-
end
-
-
1
def edit_rule_success view="overlay_rule"
-
{ id: @rule_context.name.url_key,
-
view: view }
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rule_form.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Rule; module RuleForm;
-
# Set: All rule cards (RuleForm, Buttons)
-
#
-
1
module Buttons;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rule_form/buttons.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def edit_rule_buttons
-
wrap_with(:div, class: "button-area") do
-
[
-
standard_save_button(class: "_rule-submit-button"),
-
standard_save_and_close_button(class: "_rule-submit-button", close: form_type),
-
edit_rule_cancel_button,
-
edit_rule_delete_button
-
]
-
end
-
end
-
-
1
def edit_rule_cancel_button
-
send "#{form_type}_close_button", "Cancel", situation: "secondary", class: "btn-sm"
-
end
-
-
1
def edit_rule_delete_button args={}
-
return if card.new_card?
-
-
delete_opts = {
-
confirm: delete_confirm(args[:fallback_set]),
-
# success: @edit_rule_success,
-
no_success: true,
-
"data-slotter-mode": "silent-success",
-
class: "_close-#{form_type}-on-success"
-
}
-
delete_opts["data-slot-selector"] = slot_selector if args[:slot_selector]
-
wrap_with :span, class: "rule-delete-section" do
-
delete_button delete_opts
-
end
-
end
-
-
1
def delete_confirm fallback_set
-
2
setting = card.rule_setting_name
-
-
2
if fallback_set && (fallback_set_card = Card.fetch(fallback_set))
-
"Deleting will revert to #{setting} rule for #{fallback_set_card.label}"
-
else
-
2
"Are you sure you want to delete the #{setting} rule for #{rule_set_description}?"
-
end
-
end
-
-
1
def edit_rule_submit_button
-
submit_button class: "_rule-submit-button"
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rule_form/buttons.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Rule; module RuleForm;
-
# Set: All rule cards (RuleForm, FormElements)
-
#
-
1
module FormElements;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rule_form/form_elements.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
#### DEPRECATED
-
-
1
def rule_set_selection
-
wrap_with :div, class: "set-list" do
-
[rule_set_formgroup, related_set_formgroup]
-
end
-
end
-
-
1
def rule_set_formgroup
-
tag = @rule_context.rule_user_setting_name
-
narrower = []
-
option_list "Set" do
-
rule_set_options.map do |set_name, state|
-
rule_set_radio_button set_name, tag, state, narrower
-
end
-
end
-
end
-
-
1
def related_set_formgroup
-
related_sets = related_sets_in_context
-
return "" unless related_sets&.present?
-
-
tag = @rule_context.rule_user_setting_name
-
option_list "related set" do
-
related_rule_radios related_sets, tag
-
end
-
end
-
-
1
def related_sets_in_context
-
set_context = @rule_context.rule_set_name
-
set_context && Card.fetch(set_context).prototype.related_sets
-
end
-
-
1
def option_list title
-
formgroup title, input: "set", class: "col-xs-6", help: false do
-
wrap_with :ul do
-
wrap_each_with(:li, class: "radio") { yield }
-
end
-
end
-
end
-
-
1
def related_rule_radios related_sets, tag
-
related_sets.map do |set_name, _label|
-
rule_name = "#{set_name}+#{tag}"
-
state = Card.exists?(rule_name) ? :exists : nil
-
rule_radio set_name, state do
-
radio_button :name, rule_name
-
end
-
end
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rule_form/form_elements.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Rule
-
# Set: All rule cards (RuleForm)
-
#
-
#! no set module
-
1
module RuleForm;
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rule_form/rule_set_radio.rb"; end
-
1
class RuleSetRadio
-
1
attr_reader :format
-
1
delegate :link_to_card, :radio_button, :wrap_with, :icon_tag,
-
to: :format
-
-
# @param state [:current, :overwritten]
-
1
def initialize format, set_name, tag, state
-
@format = format
-
@card = format.card
-
@set_name = set_name
-
@tag = tag
-
@state = state
-
end
-
-
1
def html narrower
-
@narrower_rules = narrower
-
-
rule_radio do
-
radio_text = "#{@set_name}+#{@tag}"
-
radio_button :name, radio_text, checked: false, warning: warning
-
end
-
end
-
-
1
private
-
-
1
def current?
-
@state == :current
-
end
-
-
1
def overwritten?
-
@state == :overwritten
-
end
-
-
1
def rule_radio
-
label_classes = ["set-label", ("current-set-label" if current?)]
-
icon = icon_tag "open_in_new", "link-muted"
-
wrap_with :label, class: label_classes.compact.join(" ") do
-
[yield, label, link_to_card(@set_name, icon, target: "decko_set")]
-
end
-
end
-
-
1
def label
-
label = Card.fetch(@set_name).label
-
label += " <em>#{extra_info}</em>".html_safe if extra_info
-
label
-
end
-
-
1
def extra_info
-
case @state
-
when :current
-
"(current)"
-
when :overwritten, :exists
-
link_to_card "#{@set_name}+#{@card.rule_user_setting_name}", "(#{@state})",
-
target: "_blank"
-
end
-
end
-
-
1
def warning
-
if @set_name == "*all"
-
"This rule will affect all cards! Are you sure?"
-
else
-
narrower_warning
-
end
-
end
-
-
# warn user if rule change won't have a effect on the current card
-
# because there is a narrower rule
-
1
def narrower_warning
-
return unless @state.in? %i[current overwritten]
-
-
@narrower_rules << Card.fetch(@set_name).uncapitalized_label
-
return unless @state == :overwritten
-
-
narrower_warning_message
-
end
-
-
1
def narrower_warning_message
-
plural = @narrower_rules.size > 1 ? "s" : ""
-
"This rule will not have any effect on this card unless you delete " \
-
"the narrower rule#{plural} for #{@narrower_rules.to_sentence}."
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rule_form/rule_set_radio.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Rule; module RuleForm;
-
# Set: All rule cards (RuleForm, SetSelection)
-
#
-
1
module SetSelection;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rule_form/set_selection.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def bridge_rule_set_selection
-
wrap_with :div, class: "set-list" do
-
bridge_rule_set_formgroup
-
end
-
end
-
-
1
def bridge_rule_set_formgroup
-
tag = @rule_context.rule_user_setting_name
-
narrower = []
-
-
bridge_option_list "Set" do
-
rule_set_options.map do |set_name, state|
-
RuleSetRadio.new(self, set_name, tag, state).html narrower
-
end
-
end
-
end
-
-
1
def bridge_option_list title
-
index = -1
-
formgroup title, input: "set", class: "col-xs-6", help: false do
-
yield.inject("") do |res, radio|
-
index += 1
-
# TODO
-
if false # index.in? [2,3]
-
wrap_with(:li, radio, class: "radio") + res
-
else
-
wrap_with :ul do
-
wrap_with(:li, (radio + res), class: "radio")
-
end
-
end
-
end
-
end
-
end
-
-
1
def rule_set_options
-
@rule_set_options ||= @rule_context.set_options
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rule_form/set_selection.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Rule
-
# Set: All rule cards (Rules)
-
#
-
1
module Rules;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rules.rb"; end
-
1
event :save_recently_edited_settings, :integrate, on: :save, changed: %i[type content] do
-
37
if (recent = Card[:recent_settings])
-
37
recent.insert_item 0, name.right
-
37
attach_subcard recent
-
end
-
end
-
-
1
def rule_set_key
-
rule_set_name.key
-
end
-
-
1
def rule_set_name
-
if is_preference?
-
name.trunk_name.trunk_name
-
else
-
name.trunk_name
-
end
-
end
-
-
1
def rule_set_pattern_name
-
rule_set_name.tag_name
-
end
-
-
1
def rule_set
-
2
if is_preference?
-
self[0..-3]
-
else
-
2
trunk
-
end
-
end
-
-
1
def rule_setting
-
19
right
-
end
-
-
1
def rule_setting_name
-
40
name.tag
-
end
-
-
1
def short_help_text
-
Card[rule_setting_name].short_help_text
-
end
-
-
1
def rule_setting_title
-
19
rule_setting_name.tr "*", ""
-
end
-
-
1
def rule_user_setting_name
-
if is_preference?
-
"#{rule_user_name}+#{rule_setting_name}"
-
else
-
rule_setting_name
-
end
-
end
-
-
# ~~~~~~~~~~ determine the set options to which a user can apply the rule.
-
1
def set_options
-
@set_options ||= [].tap do |set_options|
-
set_option_candidates.each do |set_name|
-
set_options << [set_name, state_of_set(set_name)]
-
end
-
end
-
end
-
-
# the narrowest rule should be the one attached to the set being viewed.
-
# So, eg, if you're looking at the '*all plus' set, you shouldn't
-
# have the option to create rules based on arbitrary narrower sets, though
-
# narrower sets will always apply to whatever prototype we create
-
1
def first_set_option_index candidates
-
new_card? ? 0 : candidates.index { |c| c.to_name.key == rule_set_key }
-
end
-
-
1
def set_prototype
-
if is_preference?
-
self[0..-3].prototype
-
else
-
trunk.prototype
-
end
-
end
-
-
1
private
-
-
1
def set_option_candidates
-
candidates = set_prototype.set_names
-
first = first_set_option_index candidates
-
candidates[first..-1]
-
end
-
-
1
def state_of_set set_name
-
@sets_with_existing_rules ||= 0
-
if rule_for_set? set_name
-
@sets_with_existing_rules += 1
-
state_of_existing_set
-
else
-
state_of_nonexisting_set
-
end
-
end
-
-
1
def state_of_existing_set
-
@sets_with_existing_rules == 1 ? :current : :overwritten
-
end
-
-
1
def state_of_nonexisting_set
-
@sets_with_existing_rules == 1 ? :current : :overwritten
-
end
-
-
1
def rule_for_set? set_name
-
Card.exists?("#{set_name}+#{rule_user_setting_name}")
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rules.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "ScriptRules"
-
#
-
1
module ScriptRules;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/self/script_rules.rb"; end
-
1
include_set Abstract::CodeFile
-
-
1
Self::ScriptMods.add_item :script_rules
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/self/script_rules.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Type
-
# Set: All "Set" cards
-
#
-
1
module Set;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/type/set.rb"; end
-
1
include_set Type::SearchType
-
-
1
def anchor_name
-
8
name.left_name
-
end
-
-
1
def anchor
-
Card[anchor_name]
-
end
-
-
1
def pattern_name
-
name.tag_name
-
end
-
-
1
def pattern
-
44
tag
-
end
-
-
1
def inheritable?
-
junction_only? || (anchor_name&.junction? && self_set?)
-
end
-
-
1
def self_set?
-
pattern_name == Card::Set::Self.pattern.key
-
end
-
-
1
def subclass_for_set
-
44
current_set_pattern_code = pattern.codename
-
196
Card.set_patterns.find { |set| set.pattern_code == current_set_pattern_code }
-
end
-
-
1
def junction_only?
-
@junction_only.nil? ? (@junction_only = subclass_for_set.junction_only) : @junction_only
-
end
-
-
1
def label
-
klass = subclass_for_set
-
klass ? klass.label(anchor_name) : ""
-
end
-
-
1
def uncapitalized_label
-
label = label.to_s
-
return label unless label[0]
-
-
label[0] = label[0].downcase
-
label
-
end
-
-
1
def rule_cache_key_base
-
954
if (l = left) && (r = right)
-
472
"#{l.id}+#{Codename[r.id]}"
-
else
-
482
Codename[id].to_s
-
end
-
end
-
-
1
def all_user_ids_with_rule_for setting_code
-
954
Card::Rule.all_user_ids_with_rule_for self, setting_code
-
end
-
-
1
def setting_codenames_by_group
-
result = {}
-
Card::Setting.groups.each do |group, settings|
-
visible_settings =
-
settings.reject { |s| !s || !s.applies_to_cardtype(prototype.type_id) }
-
result[group] = visible_settings.map(&:codename) unless visible_settings.empty?
-
end
-
result
-
end
-
-
1
def visible_setting_codenames
-
@visible_setting_codenames ||= visible_settings.map(&:codename)
-
end
-
-
1
def visible_settings group=nil, cardtype_id=nil
-
cardtype_id ||= prototype.type_id
-
settings =
-
(group && Card::Setting.groups[group]) || Card::Setting.groups.values.flatten.compact
-
settings.reject do |setting|
-
!setting || !setting.applies_to_cardtype(cardtype_id)
-
end
-
end
-
-
1
def broader_sets
-
prototype.set_names[1..-1]
-
end
-
-
1
def prototype
-
8
opts = subclass_for_set.prototype_args anchor_name
-
8
Card.fetch opts[:name], new: opts
-
end
-
-
1
def prototype_default_type_id
-
prototype_default_card.type_id
-
end
-
-
1
def prototype_default_card
-
prototype.rule_card(:default)
-
end
-
-
1
def related_sets with_self=false
-
if subclass_for_set.anchorless?
-
prototype.related_sets with_self
-
else
-
left(new: {}).related_sets with_self
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/type/set.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Type; module Set;
-
# Set: All "Set+HtmlViews" cards (HtmlViews)
-
#
-
1
module HtmlViews;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/type/set/html_views.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
before :open do
-
voo.hide :template_closer
-
end
-
-
1
view :core, cache: :never do
-
filtered_rule_list :bar_rule_list
-
end
-
-
1
view :nest_rules, cache: :never, unknown: true, wrap: :slot do
-
filtered_rule_list :quick_edit_rule_list, :field_related_rules, :related, mark: ""
-
end
-
-
1
view :modal_nest_rules, cache: :never, unknown: true,
-
wrap: { modal: { title: "Rules for nest" } } do
-
filtered_rule_list :quick_edit_rule_list, :field_related_rules, :self
-
end
-
-
1
view :bridge_rules_tab, cache: :never do
-
filtered_rule_list :pill_rule_list, :common, :related, mark: ""
-
end
-
-
1
def filtered_rule_list view, *filter_args
-
[rules_filter(view, *filter_args),
-
render(view)]
-
end
-
-
1
view :set_label do
-
wrap_with :strong, card.label, class: "set-label"
-
end
-
-
1
Card::Setting.groups.each_key do |group_key|
-
7
view group_key.to_sym do
-
next unless card.visible_settings(group_key).present?
-
-
haml :group_panel, group_key: group_key
-
end
-
end
-
-
1
def setting_group default=:common
-
voo&.filter&.to_sym || params[:group]&.to_sym || default
-
end
-
-
1
view :input do
-
"Cannot currently edit Sets" # LOCALIZE
-
end
-
-
1
view :one_line_content, wrap: {} do
-
""
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/type/set/html_views.rb ~~
-
# -*- encoding : utf-8 -*-
-
5
class Card; module Set; class Type; module Set;; module HtmlViews;
-
# Set: All "Set+HtmlViews" cards (HtmlViews, RuleLists)
-
#
-
1
module RuleLists;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/type/set/html_views/rule_lists.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :quick_edit_rule_list, cache: :never, wrap: { slot: { class: "rule-list" } } do
-
quick_edit_rule_list setting_list_from_params(:field_related)
-
end
-
-
1
view :bar_rule_list, cache: :never, wrap: { slot: { class: "rule-list" } } do
-
bar_rule_list setting_list_from_params
-
end
-
-
1
view :pill_rule_list, cache: :never, wrap: { slot: { class: "rule-list" } } do
-
pill_rule_list setting_list_from_params
-
end
-
-
1
def quick_edit_rule_list settings
-
list_tag class: "nav nav-pills flex-column bridge-pills" do
-
settings.map { |setting| rule_list_item setting, :quick_edit }
-
end
-
end
-
-
1
def pill_rule_list settings
-
list_items =
-
settings.map { |setting| rule_list_item setting, :rule_bridge_link }
-
bridge_pills list_items
-
end
-
-
1
def bar_rule_list settings
-
list_items =
-
settings.map { |setting| rule_list_item setting, :bar, hide: :full_name }
-
list_items.join("\n").html_safe
-
end
-
-
1
def rule_list_item setting, view, opts={}
-
return "" unless show_view? setting
-
-
rule_card = card.fetch setting, new: {}
-
nest(rule_card, opts.merge(view: view)).html_safe
-
end
-
-
1
def setting_list_from_params default=:common
-
setting_list setting_group(default)
-
end
-
end
-
end;end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/type/set/html_views/rule_lists.rb ~~
-
# -*- encoding : utf-8 -*-
-
5
class Card; module Set; class Type; module Set;; module HtmlViews;
-
# Set: All "Set+HtmlViews" cards (HtmlViews, Template)
-
#
-
1
module Template;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/type/set/html_views/template.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :template_link, cache: :never do
-
21
wrap do
-
21
voo.title = parent.voo.nest_syntax if parent
-
21
"{{#{link_to_template_editor}}}"
-
end
-
end
-
-
1
def link_to_template_editor
-
21
link_to_view :modal_nest_rules, voo.title
-
end
-
end
-
end;end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/type/set/html_views/template.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Type; module Set;
-
# Set: All "Set+RulesFilter" cards (RulesFilter)
-
#
-
1
module RulesFilter;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/type/set/rules_filter.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def rules_filter view, selected_setting=nil, set_options=nil, path_opts={}
-
form_tag path(path_opts.merge(view: view)),
-
remote: true, method: "get", role: "filter",
-
"data-slot-selector": ".card-slot.rule-list",
-
class: classy("nodblclick slotter form-inline slim-select2 m-2") do
-
output [
-
label_tag(:view, icon_tag("filter_list"), class: "mr-2"),
-
setting_select(selected_setting),
-
set_select(set_options)
-
].flatten
-
end
-
end
-
-
1
def set_select set_options
-
return filter_text.html_safe unless set_options
-
-
[content_tag(:span, "rules that apply to set ...", class: "mx-2 small"),
-
set_select_tag(set_options)]
-
end
-
-
1
def setting_select selected=nil
-
select_tag(:group, grouped_options_for_select(setting_options, selected),
-
class: "_submit-on-select form-control",
-
"data-select2-id": "#{unique_id}-#{Time.now.to_i}")
-
end
-
-
1
def filter_text
-
wrap_with :span, class: "mx-2 small" do
-
"rules that apply to #{_render_set_label.downcase}" # LOCALIZE
-
end
-
end
-
-
1
def set_select_tag set_options=:related
-
select_tag(:mark, set_select_options(set_options),
-
class: "_submit-on-select form-control _close-rule-overlay-on-select",
-
"data-minimum-results-for-search": "Infinity",
-
"data-select2-id": "#{unique_id}-#{Time.now.to_i}")
-
end
-
-
1
def selected_set
-
params[:set]
-
end
-
-
1
def set_select_options set_options
-
options =
-
if set_options == :related
-
related_set_options
-
else
-
[[card.label, card.name.url_key]]
-
end
-
options_for_select(options, selected_set)
-
end
-
-
1
def related_set_options
-
card.related_sets(true).map do |name, label|
-
[label, name.to_name.url_key]
-
end
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/type/set/rules_filter.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class Type; module Set;
-
# Set: All "Set+SettingLists" cards (SettingLists)
-
#
-
1
module SettingLists;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/type/set/setting_lists.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
SETTING_OPTIONS = [["Common", :common_rules],
-
["All", :all_rules],
-
["Field", :field_related_rules],
-
["Recent", :recent_rules]].freeze
-
-
1
COMMON_SETTINGS = %i[create read update delete structure default guide].freeze
-
1
FIELD_SETTINGS = %i[default help].freeze
-
-
1
def setting_options
-
[["Categories", SETTING_OPTIONS],
-
["Groups", Card::Setting.group_names.keys],
-
["Single rules", card.visible_setting_codenames]]
-
end
-
-
1
def field_settings
-
%i[default help input_type content_options content_option_view]
-
end
-
-
# @param val setting category, setting group or single setting
-
1
def setting_list val
-
category_setting_list(val) || group_setting_list(val) || [val]
-
end
-
-
1
def group_setting_list group
-
card.visible_settings(group).map(&:codename) if Card::Setting.groups[group]
-
end
-
-
1
def category_setting_list cat
-
case cat
-
when :all, :all_rules
-
card.visible_setting_codenames.sort
-
when :recent, :recent_rules
-
recent_settings
-
when :common, :common_rules
-
card.visible_setting_codenames & COMMON_SETTINGS
-
when :field_related, :field_related_rules
-
field_related_settings
-
when :nest_editor_field_related
-
nest_editor_field_related_settings
-
end
-
end
-
-
1
def nest_editor_field_related_settings
-
field_settings
-
# & card.visible_settings(nil, card.prototype_default_type_id).map(&:codename)
-
end
-
-
1
def field_related_settings
-
field_settings # card.visible_setting_codenames &
-
end
-
-
1
def recent_settings
-
recent_settings = Card[:recent_settings].item_cards.map(&:codename)
-
recent_settings.map(&:to_sym) & card.visible_setting_codenames
-
end
-
-
1
view :all_rules_list do
-
pill_rule_list card.visible_setting_codenames.sort
-
end
-
-
1
view :recent_rules_list do
-
recent_settings = Card[:recent_settings].item_cards.map(&:codename)
-
settings = recent_settings.map(&:to_sym) & card.visible_setting_codenames
-
pill_rule_list settings
-
end
-
-
1
view :common_rules_list do
-
settings = card.visible_setting_codenames & COMMON_SETTINGS # "&" = set intersection
-
pill_rule_list settings
-
end
-
-
1
view :field_related_rules_list do
-
pill_rule_list field_related_settings
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/type/set/setting_lists.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (ReferenceEditor)
-
#
-
# shared helper methods for link editor and nest editor
-
1
module ReferenceEditor;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/reference_editor.rb"; end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def tinymce_id
-
params[:tinymce_id]
-
end
-
-
1
def tm_param key
-
params[:"tm_snippet_#{key}"]
-
end
-
-
1
def modal_tm_snippet_editor?
-
@tm_snippet_editor_mode != :overlay
-
end
-
-
1
private
-
-
1
def apply_tm_snippet_data snippet
-
{ "data-tinymce-id": tinymce_id }.tap do |data|
-
apply_tm_snippet_var(data, :start) { tm_param :start }
-
apply_tm_snippet_var(data, :size, :raw) { snippet.raw.size }
-
data["data-dismiss"] = "modal" if modal_tm_snippet_editor?
-
data["data-index"] = params["index"] if params["index"].present?
-
end
-
end
-
-
1
def apply_tm_snippet_var data, varname, paramname=nil
-
return unless tm_param(paramname || varname).present?
-
-
data[:"data-tm-snippet-#{varname}"] = yield
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/reference_editor.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module ReferenceEditor;
-
# Set: All cards (ReferenceEditor, LinkEditor)
-
#
-
1
module LinkEditor;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/reference_editor/link_editor.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :link_editor, cache: :never, unknown: true,
-
wrap: {
-
slot: { class: "_overlay d0-card-overlay card nodblclick" }
-
} do
-
link_editor :overlay
-
end
-
-
1
view :modal_link_editor, cache: :never, unknown: true,
-
wrap: { slot: { class: "nodblclick" } } do
-
modal_link_editor
-
end
-
-
1
def link_editor editor_mode
-
@tm_snippet_editor_mode = editor_mode
-
haml :reference_editor, ref_type: :link, editor_mode: @tm_snippet_editor_mode,
-
apply_opts: link_apply_opts, snippet: link_snippet
-
end
-
-
1
def modal_link_editor
-
wrap_with :modal do
-
link_editor :modal
-
end
-
end
-
-
1
def link_snippet
-
@link_snippet ||= LinkParser.new params[:tm_snippet_raw]
-
end
-
-
1
def link_apply_opts
-
apply_tm_snippet_data link_snippet
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/reference_editor/link_editor.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module ReferenceEditor;
-
# Set: All cards (ReferenceEditor, LinkEditor)
-
#
-
#! no set module
-
-
1
module LinkEditor;
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/reference_editor/link_editor/link_parser.rb"; end
-
# Extracts all information needed to generate the link editor form
-
# from a link syntax string
-
1
class LinkParser
-
1
attr_reader :name, :options, :field, :raw
-
-
1
def self.new link_string
-
return super if link_string.is_a? String
-
-
OpenStruct.new(name: "", options: {}, raw: "[[ ]]")
-
end
-
-
1
def initialize link_string
-
@raw = link_string
-
link = Card::Content::Chunk::Link.new link_string, nil
-
init_name link.name
-
@options = link.options
-
end
-
-
1
def title
-
@options && @options[:title]
-
end
-
-
1
def field?
-
@field
-
end
-
-
1
private
-
-
1
def init_name name
-
@field = name.to_name.simple_relative?
-
@name = @field ? name.to_s[1..-1] : name
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/reference_editor/link_editor/link_parser.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module ReferenceEditor;
-
# Set: All cards (ReferenceEditor, NestEditor)
-
#
-
1
module NestEditor;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/reference_editor/nest_editor.rb"; end
-
-
1
def left_type_for_nest_editor_set_selection
-
type_name
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
# Card::View::Options.shark_keys - %i[nest_syntax nest_name items cache]
-
# TODO: connect to Card::View::Options
-
# (that way a mod can add an option that becomes available to nests)
-
-
1
view :nest_editor, cache: :never, unknown: true,
-
wrap: {
-
slot: { class: "_overlay d0-card-overlay card nodblclick" }
-
} do
-
nest_editor :overlay
-
end
-
-
1
view :modal_nest_editor, cache: :never, unknown: true,
-
wrap: { slot: { class: "nodblclick" } } do
-
modal_nest_editor
-
end
-
-
1
view :nest_content, perms: :create, cache: :never, unknown: true, wrap: :slot do
-
if card.known?
-
known_nest_content
-
else
-
unknown_nest_content
-
end
-
end
-
-
1
def nest_editor editor_mode
-
@tm_snippet_editor_mode = editor_mode
-
voo.hide :content_tab unless show_content_tab?
-
haml :reference_editor, ref_type: :nest, editor_mode: @tm_snippet_editor_mode,
-
apply_opts: nest_apply_opts,
-
snippet: nest_snippet
-
end
-
-
1
def nest_editor_tabs
-
tab_hash = {}
-
tab_hash[:content] = nest_content_tab if voo.show? :content_tab
-
tab_hash.merge! options: haml(:_options, snippet: nest_snippet),
-
rules: nest_rules_tab,
-
help: haml(:_help)
-
tabs tab_hash, default_active_tab
-
end
-
-
1
def show_content_tab?
-
!card.is_structure?
-
end
-
-
1
def default_active_tab
-
voo.show?(:content_tab) ? :content : :options
-
end
-
-
1
def nest_content_tab
-
name_dependent_slot do
-
@nest_content_tab || nest(card.name.field(nest_snippet.name),
-
view: :nest_content, hide: :guide)
-
end
-
end
-
-
1
def nest_rules_tab
-
name_dependent_slot do
-
nest(set_name_for_nest_rules, view: :nest_rules)
-
end
-
end
-
-
1
def name_dependent_slot
-
result = [empty_nest_name_alert(nest_snippet.name.blank?)]
-
result <<
-
if nest_snippet.name.blank?
-
content_tag :div, "", class: "card-slot" # placeholder
-
else
-
yield
-
end
-
result
-
end
-
-
1
def empty_nest_name_alert show
-
alert :warning, false, false,
-
class: "mb-0 _empty-nest-name-alert #{'d-none' unless show}" do
-
"nest name required" # LOCALIZE
-
end
-
end
-
-
1
def modal_nest_editor
-
wrap_with :modal do
-
nest_editor :modal
-
end
-
end
-
-
1
def nest_snippet
-
@nest_snippet ||=
-
Card::Reference::NestParser.new params[:tm_snippet_raw],
-
default_nest_view, default_item_view
-
end
-
-
1
def set_name_for_nest_rules
-
nest_name = nest_snippet.name
-
if (type_name = card.left_type_for_nest_editor_set_selection)
-
[type_name, nest_name, :type_plus_right]
-
else
-
[nest_name, :right]
-
end
-
end
-
-
1
def default_nest_editor_item_options
-
[[:view, default_item_view]]
-
end
-
-
1
def nest_option_name_select selected=nil, level=0
-
classes = "form-control form-control-sm _nest-option-name"
-
classes += " _new-row" unless selected
-
select_tag "nest_option_name_#{unique_id}",
-
nest_option_name_select_options(selected, level),
-
class: classes, id: nil
-
# id: nil ensures that select2 generates its own unique identifier
-
# that ensures that we can clone this tag without breaking select2
-
end
-
-
1
def nest_option_name_select_options selected, level
-
options = selected ? [] : ["--"]
-
options += Card::Reference::NestParser::NEST_OPTIONS
-
options_for_select(
-
options, disabled: nest_option_name_disabled(selected, level),
-
selected: selected
-
)
-
end
-
-
1
def nest_option_name_disabled selected, level
-
disabled = nest_option_name_disabled_options level
-
disabled = disabled&.map(&:first)
-
disabled&.delete selected if selected
-
disabled
-
end
-
-
1
def nest_option_name_disabled_options level
-
if level == 0
-
nest_snippet.options
-
else
-
nest_snippet.item_options[level - 1] || default_nest_editor_item_options
-
end
-
end
-
-
1
def nest_apply_opts
-
apply_tm_snippet_data nest_snippet
-
end
-
-
1
def nest_option_value_select value=nil
-
# select_tag "nest_option_value_#{unique_id}"
-
text_field_tag "value", value,
-
class: "_nest-option-value form-control form-control-sm",
-
disabled: !value,
-
id: nil
-
end
-
-
1
def known_nest_content
-
voo.hide! :cancel_button
-
add_name_context
-
with_nest_mode :edit do
-
frame do
-
[
-
render_edit_inline
-
]
-
end
-
end
-
end
-
-
1
def unknown_nest_content
-
voo.hide! :guide
-
voo.show! :new_type_formgroup
-
new_view_frame_and_form buttons: new_image_buttons,
-
success: { tinymce_id: Env.params[:tinymce_id] }
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/reference_editor/nest_editor.rb ~~
-
# -*- encoding : utf-8 -*-
-
4
class Card; module Set; class All; module ReferenceEditor;
-
# Set: All cards (ReferenceEditor, NestImage)
-
#
-
1
module NestImage;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/reference_editor/nest_image.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :nest_image, unknown: true, cache: :never,
-
wrap: {
-
slot: { class: "_overlay d0-card-overlay card nodblclick" }
-
} do
-
nest_image_editor :overlay
-
end
-
-
1
view :modal_nest_image, unknown: true, cache: :never,
-
wrap: { slot: { class: "nodblclick" } } do
-
nest_image_editor :modal
-
end
-
-
1
view :new_image, perms: :create, unknown: true, cache: :never do
-
new_view_frame_and_form new_image_form_opts
-
end
-
-
1
def nest_image_editor editor_mode
-
adapt_reference_editor_for_images
-
nest_editor editor_mode
-
end
-
-
-
1
def adapt_reference_editor_for_images
-
nest_name = card.autoname(card.name.field("image01"))
-
voo.show! :content_tab
-
@nest_content_tab = nest(nest_name, view: :new_image, type: :image, hide: :guide)
-
-
image_name = nest_name.to_name.right
-
@nest_snippet = Card::Reference::NestParser.new_image image_name
-
end
-
-
-
1
def new_image_form_opts
-
{ buttons: new_image_buttons,
-
success: { tinymce_id: Env.params[:tinymce_id],
-
view: :open } }
-
end
-
-
1
def new_image_buttons
-
button_formgroup do
-
[standard_save_button(no_origin_update: true, class: "_change-create-to-update")]
-
end
-
end
-
end
-
-
2
module JsFormat; module_parent.send :register_set_format, Card::Format::JsFormat, self; extend Card::Set::AbstractFormat
-
1
view :change_create_to_update, unknown: true do
-
tm_id = if Env.params[:tinymce_id].present?
-
"\"#{Env.params[:tinymce_id]}\""
-
else
-
'$(".tinymce-textarea").attr("id")'
-
end
-
<<-JAVASCRIPT.strip_heredoc
-
nest.changeCreateToUpdate(#{tm_id});
-
JAVASCRIPT
-
end
-
-
1
view :open_nest_editor, unknown: true do
-
tm_id = if Env.params[:tinymce_id].present?
-
"\"#{Env.params[:tinymce_id]}\""
-
else
-
'$(".tinymce-textarea").attr("id")'
-
end
-
<<-JAVASCRIPT.strip_heredoc
-
tm = tinymce.get(#{tm_id});
-
nest.insertNest(tm, "{{+#{card.name.tag}|view: content; size: medium}}");
-
JAVASCRIPT
-
end
-
end
-
end;end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/reference_editor/nest_image.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (TinymceEditor)
-
#
-
1
module TinymceEditor;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/tinymce_editor.rb"; end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
def tinymce_editor_input
-
56
text_area :content, rows: 3, class: "tinymce-textarea d0-card-content",
-
id: unique_id
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/tinymce_editor.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "ScriptTinymce"
-
#
-
1
module ScriptTinymce;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/self/script_tinymce.rb"; end
-
1
include_set Abstract::CodeFile
-
-
1
Self::ScriptMods.add_item :script_tinymce
-
1
Self::InputOptions.add_to_basket :options, "tinymce editor"
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/self/script_tinymce.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "ScriptTinymceConfig"
-
#
-
1
module ScriptTinymceConfig;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/self/script_tinymce_config.rb"; end
-
1
include_set Abstract::CodeFile
-
-
1
Self::ScriptEditors.add_item :script_tinymce_config
-
1
All::Head::HtmlFormat.add_to_basket :mod_js_config, [:tiny_mce, "setTinyMCEConfig"]
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/self/script_tinymce_config.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Self
-
# Set: The card "TinyMce"
-
#
-
1
module TinyMce;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/self/tiny_mce.rb"; end
-
1
def raw_help_text
-
<<-TEXT
-
Configure [[http://tinymce.com|TinyMCE]], Decko's default
-
[[http://en.wikipedia.org/wiki/Wysiwyg|wysiwyg]] editor.
-
[[http://decko.org/TinyMCE|more]]
-
TEXT
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/self/tiny_mce.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (EventViz)
-
#
-
# the events method is a developer's tool for visualizing the event order
-
# for a given card.
-
# For example, from a console you might run
-
#
-
# puts mycard.events :update
-
#
-
# to see the order of events that will be executed on mycard.
-
1
module EventViz;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-monkey/set/all/event_viz.rb"; end
-
# The indention and arrows (^v) indicate event dependencies.
-
#
-
# Note: as of yet, the functionality is a bit rough. It does not display events
-
# that are called directly from within other events,
-
# and certain event requirements (like the presence of an 'act') may
-
# prevent events from showing up in the tree.
-
1
def events action
-
@action = action
-
events = Director::Stages::STAGES.map { |stage| events_tree "#{stage}_stage" }
-
@action = nil
-
print_events events
-
end
-
-
1
def events_tree filt
-
try("_#{filt}_callbacks")&.each_with_object({ name: filt }) do |callback, hash|
-
events_branch hash, callback.kind, callback.filter if callback.applies? self
-
end
-
end
-
-
1
private
-
-
1
def print_events events, prefix="", depth=0
-
depth += 1
-
space = " " * (depth * 2)
-
text = ""
-
events.each do |event|
-
text += print_event_pre event, depth, space
-
text += print_event_main event, prefix
-
text += print_event_post event, depth, space
-
end
-
text
-
end
-
-
1
def print_event_pre event, depth, space
-
if event[:before]
-
print_events event[:before], space + "v ", depth
-
elsif event[:around]
-
print_events event[:around], space + "vv ", depth
-
else
-
""
-
end
-
end
-
-
1
def print_event_main event, prefix
-
"#{prefix}#{event[:name]}\n"
-
end
-
-
1
def print_event_post event, depth, space
-
return "" unless event[:after]
-
-
print_events event[:after], space + "^ ", depth
-
end
-
-
1
def events_branch hash, kind, filter
-
hash[kind] ||= []
-
hash[kind] << events_tree(filter)
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-monkey/set/all/event_viz.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class All
-
# Set: All cards (ViewViz)
-
#
-
1
module ViewViz;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-monkey/set/all/view_viz.rb"; end
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :views_by_format do
-
format_views =
-
self.class.ancestors.each_with_object({}) do |format_class, hash|
-
views =
-
format_class.instance_methods.map do |method|
-
next unless method.to_s =~ /^_view_(.+)$/
-
Regexp.last_match(1)
-
end.compact
-
next unless views.present?
-
format_class.name =~ /^Card(::Set)?::(.+?)$/ #::(\w+Format)
-
hash[Regexp.last_match(2)] = views
-
end
-
accordion_group format_views
-
end
-
-
1
view :views_by_name do
-
views = methods.map do |method|
-
Regexp.last_match(1) if method.to_s.match?(/^_view_(.+)$/)
-
end.compact.sort
-
list_group views
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-monkey/set/all/view_viz.rb ~~
-
# -*- encoding : utf-8 -*-
-
3
class Card; module Set; class Right
-
# Set: All "+Debug" cards
-
#
-
1
module Debug;
-
1
extend Card::Set
-
1
def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-monkey/set/right/debug.rb"; end
-
1
def virtual?
-
new?
-
end
-
-
2
module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
-
1
view :core do
-
core_section_config(card.left).map do |item|
-
section(*item)
-
end
-
end
-
-
1
def core_section_config subject
-
[["Sets", tabs("set modules" => set_modules_accordion(subject),
-
"all modules" => singleton_modules_list(subject),
-
"patterns" => set_patterns_breadcrumb(subject))],
-
["Views", tabs("by format" => subformat(subject)._render_views_by_format,
-
"by name" => subformat(subject)._render_views_by_name)],
-
["Events", tabs(create: "<pre>#{subject.events(:create)}</pre>",
-
update: "<pre>#{subject.events(:update)}</pre>",
-
delete: "<pre>#{subject.events(:delete)}</pre>")],
-
["Cache/DB Comparison", cache_comparison_table(subject)]]
-
end
-
-
# rubocop:disable AccessorMethodName
-
1
def set_modules_accordion subject
-
sets = subject.set_modules.each_with_object({}) do |sm, hash|
-
ans = sm.ancestors
-
ans.shift
-
hash[sm.to_s] = ans
-
end
-
accordion_group sets
-
end
-
-
1
def set_patterns_breadcrumb subject
-
links = subject.patterns.reverse.map { |pattern| link_to_card pattern.to_s }
-
breadcrumb links
-
end
-
# rubocop:enable AccessorMethodName
-
-
1
def singleton_modules_list subject
-
all_mods = subject.singleton_class.ancestors.map(&:to_s)
-
all_mods.shift
-
list_group all_mods
-
end
-
-
1
def cache_comparison_table subject
-
cache_card = Card.fetch(subject.key)
-
db_card = Card.find_by_key(subject.key)
-
return unless cache_card && db_card
-
table(
-
%i[name updated_at updater_id content inspect].map do |field|
-
[field.to_s,
-
h(cache_card.send(field)),
-
h(db_card.send(field))]
-
end,
-
header: ["Field", "Cache Val", "Database Val"]
-
)
-
end
-
-
1
def section title, content
-
%(
-
<h2>#{title}</h2>
-
#{content}
-
)
-
end
-
-
1
def class_locations klass
-
methods = defined_methods(klass)
-
file_groups = methods.group_by { |sl| sl[0] }
-
file_counts = file_groups.map do |file, sls|
-
lines = sls.map { |sl| sl[1] }
-
count = lines.size
-
line = lines.min
-
{ file: file, count: count, line: line }
-
end
-
file_counts.sort_by! { |fc| fc[:count] }
-
file_counts.map { |fc| [fc[:file], fc[:line]] }
-
end
-
-
1
def defined_methods klass
-
methods =
-
klass.methods(false).map { |m| klass.method(m) } +
-
klass.instance_methods(false).map { |m| klass.instance_method(m) }
-
methods.map!(&:source_location)
-
methods.compact!
-
methods
-
end
-
end
-
end;end;end;end;
-
# ~~ generated from /Users/ethan/dev/decko/gem/card-mod-monkey/set/right/debug.rb ~~
-
# -*- encoding : utf-8 -*-
-
# Set Pattern: All
-
#
-
1
class Card::Set::All < Card::Set::Pattern::Base
-
1
extend Card::Set::Pattern::Helper
-
1
cattr_accessor :options
-
1
class << self
-
1
def label _name
-
140
"All cards"
-
end
-
-
1
def short_label _name
-
"everything"
-
end
-
-
1
def prototype_args _anchor
-
{}
-
end
-
end
-
1
register "All".underscore.to_sym, (options || {})
-
end
-
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set_pattern/01_all.rb ~~
-
# -*- encoding : utf-8 -*-
-
# Set Pattern: AllPlus
-
#
-
1
class Card::Set::AllPlus < Card::Set::Pattern::Base
-
1
extend Card::Set::Pattern::Helper
-
1
cattr_accessor :options
-
1
class << self
-
1
@@options = { junction_only: true }
-
-
1
def label _name
-
'All "+" cards'
-
end
-
-
1
def short_label _name
-
'all "+" cards'
-
end
-
-
1
def prototype_args _anchor
-
{ name: "+" }
-
end
-
end
-
1
register "AllPlus".underscore.to_sym, (options || {})
-
end
-
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set_pattern/02_all_plus.rb ~~
-
# -*- encoding : utf-8 -*-
-
# Set Pattern: Type
-
#
-
1
class Card::Set::Type < Card::Set::Pattern::Base
-
1
extend Card::Set::Pattern::Helper
-
1
cattr_accessor :options
-
1
class << self
-
1
load "card/set/type.rb" # "load" not "require" so pattern reloads properly
-
-
1
def label name
-
53
%(All "#{name}" cards)
-
end
-
-
1
def short_label name
-
14
%(all "#{name}s")
-
end
-
-
1
def generic_label
-
"cards of a given type"
-
end
-
-
1
def prototype_args anchor
-
4
{ type: anchor }
-
end
-
-
1
def pattern_applies? card
-
7674
!!card.type_id
-
end
-
-
1
def anchor_name card
-
6411
card.type_name
-
end
-
-
1
def anchor_id card
-
6411
card.type_id
-
end
-
end
-
1
register "Type".underscore.to_sym, (options || {})
-
end
-
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set_pattern/03_type.rb ~~
-
# -*- encoding : utf-8 -*-
-
# Set Pattern: Star
-
#
-
1
class Card::Set::Star < Card::Set::Pattern::Base
-
1
extend Card::Set::Pattern::Helper
-
1
cattr_accessor :options
-
1
class << self
-
1
def label _name
-
'All "*" cards'
-
end
-
-
1
def short_label _name
-
'all "*" cards'
-
end
-
-
1
def prototype_args _anchor
-
{ name: "*dummy" }
-
end
-
-
1
def pattern_applies? card
-
7674
card.name.star?
-
end
-
end
-
1
register "Star".underscore.to_sym, (options || {})
-
end
-
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set_pattern/04_star.rb ~~
-
# -*- encoding : utf-8 -*-
-
# Set Pattern: Rstar
-
#
-
1
class Card::Set::Rstar < Card::Set::Pattern::Base
-
1
extend Card::Set::Pattern::Helper
-
1
cattr_accessor :options
-
1
class << self
-
1
@@options = { junction_only: true }
-
-
1
def label _name
-
1
'All "+*" cards'
-
end
-
-
1
def short_label _name
-
'all "+*" cards'
-
end
-
-
1
def prototype_args _anchor
-
{ name: "*dummy+*dummy" }
-
end
-
-
1
def pattern_applies? card
-
7674
card.name.rstar?
-
end
-
end
-
1
register "Rstar".underscore.to_sym, (options || {})
-
end
-
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set_pattern/05_rstar.rb ~~
-
# -*- encoding : utf-8 -*-
-
# Set Pattern: Rule
-
#
-
1
class Card::Set::Rule < Card::Set::Pattern::Base
-
1
extend Card::Set::Pattern::Helper
-
1
cattr_accessor :options
-
1
class << self
-
1
@@options = { junction_only: true }
-
-
1
def label _name
-
11
"All rule cards"
-
end
-
-
1
def short_label _name
-
"all rule cards"
-
end
-
-
1
def prototype_args _anchor
-
{ name: "*all+*create" }
-
end
-
-
1
def pattern_applies? card
-
7674
card.is_rule?
-
end
-
end
-
1
register "Rule".underscore.to_sym, (options || {})
-
end
-
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set_pattern/06_rule.rb ~~
-
# -*- encoding : utf-8 -*-
-
# Set Pattern: Right
-
#
-
1
class Card::Set::Right < Card::Set::Pattern::Base
-
1
extend Card::Set::Pattern::Helper
-
1
cattr_accessor :options
-
1
class << self
-
@@options = {
-
1
junction_only: true,
-
assigns_type: true
-
}
-
-
1
def label name
-
60
%(All "+#{name}" cards)
-
end
-
-
1
def short_label name
-
%(all "+#{name}s")
-
end
-
-
1
def generic_label
-
"given field cards"
-
end
-
-
1
def prototype_args anchor
-
{ name: "*dummy+#{anchor}" }
-
end
-
-
1
def anchor_name card
-
4380
card.name.tag
-
end
-
end
-
1
register "Right".underscore.to_sym, (options || {})
-
end
-
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set_pattern/07_right.rb ~~
-
# -*- encoding : utf-8 -*-
-
# Set Pattern: TypePlusRight
-
#
-
# Patterned field names on a specific type
-
1
class Card::Set::TypePlusRight < Card::Set::Pattern::Base
-
1
extend Card::Set::Pattern::Helper
-
1
cattr_accessor :options
-
1
class << self
-
-
@@options = {
-
1
junction_only: true,
-
assigns_type: true,
-
anchor_parts_count: 2
-
}
-
-
1
def label name
-
4
name = name.to_name
-
4
%(All "+#{name.tag}" cards on "#{name.left}" cards)
-
end
-
-
1
def short_label name
-
name = name.to_name
-
%(all "+#{name.tag}" on "#{name.left}s")
-
end
-
-
1
def generic_label
-
"given field cards on a given type"
-
end
-
-
1
def prototype_args anchor
-
{
-
name: "+#{anchor.tag}",
-
supercard: Card.new(name: "*dummy", type: anchor.trunk_name)
-
}
-
end
-
-
1
def anchor_name card
-
4380
"#{left_type(card)}+#{card.name.tag}"
-
end
-
end
-
1
register "TypePlusRight".underscore.to_sym, (options || {})
-
end
-
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set_pattern/08_type_plus_right.rb ~~
-
# -*- encoding : utf-8 -*-
-
# Set Pattern: Self
-
#
-
1
class Card::Set::Self < Card::Set::Pattern::Base
-
1
extend Card::Set::Pattern::Helper
-
1
cattr_accessor :options
-
1
class << self
-
1
def label name
-
102
%(The card "#{name}")
-
end
-
-
1
def short_label name
-
22
name
-
end
-
-
1
def generic_label
-
"a single card"
-
end
-
-
1
def prototype_args anchor
-
4
{ name: anchor }
-
end
-
-
1
def anchor_name card
-
7674
card.name
-
end
-
-
1
def anchor_id card
-
7674
card.id
-
end
-
end
-
1
register "Self".underscore.to_sym, (options || {})
-
end
-
-
# ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set_pattern/09_self.rb ~~
-
# -*- encoding : utf-8 -*-
-
-
1
require "active_support/inflector"
-
1
require "htmlentities"
-
-
1
class Cardname < String
-
1
require_relative "cardname/parts"
-
1
require_relative "cardname/variants"
-
1
require_relative "cardname/contextual"
-
1
require_relative "cardname/predicates"
-
1
require_relative "cardname/manipulate"
-
-
1
include Parts
-
1
include Variants
-
1
include Contextual
-
1
include Predicates
-
1
include Manipulate
-
-
1
OK4KEY_RE = '\p{Word}\*'
-
-
1
cattr_accessor :joint, :banned_array, :var_re, :uninflect, :params,
-
:session, :stabilize
-
-
1
self.joint = "+"
-
1
self.banned_array = []
-
1
self.var_re = /\{([^\}]*\})\}/
-
1
self.uninflect = :singularize
-
1
self.stabilize = false
-
-
1
JOINT_RE = Regexp.escape joint
-
-
1
class << self
-
1
def cache
-
505226
@cache ||= {}
-
end
-
-
1
def new obj
-
503657
return obj if obj.is_a? self.class
-
-
503657
str = stringify(obj)
-
503657
cached_name(str) || super(str)
-
end
-
-
1
def cached_name str
-
503657
cache[str]
-
end
-
-
1
def reset_cache str=nil
-
31
str ? cache.delete(str) : @cache = {}
-
end
-
-
1
def stringify obj
-
503657
if obj.is_a?(Array)
-
obj.map(&:to_s) * joint
-
else
-
503657
obj.to_s
-
end
-
end
-
-
1
def nothing_banned?
-
137
return @nothing_banned unless @nothing_banned.nil?
-
-
1
@nothing_banned = banned_array.empty?
-
end
-
-
1
def banned_re
-
@banned_re ||= /[#{Regexp.escape((banned_array + [joint])).join}]/
-
end
-
-
# Sometimes the core rule "the key's key must be itself" (called "stable" below) is violated
-
# eg. it fails with singularize as uninflect method for Matthias -> Matthia -> Matthium
-
# Usually that means the name is a proper noun and not a plural.
-
# You can choose between two solutions:
-
# 1. don't uninflect if the uninflected key is not stable (stabilize = false)
-
# 2. uninflect until the key is stable (stabilize = true)
-
1
def stable_key name
-
2865
key_one = name.send(uninflect)
-
2865
key_two = key_one.send(uninflect)
-
2865
return key_one unless key_one != key_two
-
-
stabilize ? stable_key(key_two) : name
-
end
-
-
1
def dangerous_methods
-
321
bang_methods = String.instance_methods.select { |m| m.to_s.ends_with?("!") }
-
1
%i[replace concat clear].concat bang_methods
-
end
-
-
1
def split_parts str
-
215389
str.split(/\s*#{JOINT_RE}\s*/, -1)
-
end
-
end
-
-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
# ~~~~~~~~~~~~~~~~~~~~~~ INSTANCE ~~~~~~~~~~~~~~~~~~~~~~~~~
-
1
attr_reader :key
-
-
1
def initialize str
-
1538
self.class.cache[str] = super str.strip.encode("UTF-8")
-
end
-
-
1
def s
-
334875
@s ||= String.new self
-
end
-
1
alias_method :to_s, :s
-
1
alias_method :to_str, :s
-
-
1
def to_name
-
103858
self
-
end
-
-
1
dangerous_methods.each do |m|
-
32
define_method m do |*args, &block|
-
31
reset
-
31
super(*args, &block)
-
end
-
end
-
-
# dangerous, too
-
1
def []= index, val
-
p = parts
-
p[index] = val
-
replace self.class.new(p)
-
end
-
-
1
def << val
-
replace self.class.new(parts << val)
-
end
-
-
1
def key
-
164125
@key ||= part_keys.join(self.class.joint)
-
end
-
-
1
def == other
-
other_key =
-
case
-
9926
when other.respond_to?(:key) then other.key
-
1092
when other.respond_to?(:to_name) then other.to_name.key
-
else other.to_s
-
end
-
5509
other_key == key
-
end
-
-
1
private
-
-
1
def reset
-
31
self.class.reset_cache s
-
31
instance_variables.each do |var|
-
73
instance_variable_set var, nil
-
end
-
end
-
end
-
1
class Cardname
-
1
module Contextual
-
1
RELATIVE_REGEXP = /\b_(left|right|whole|self|user|main|\d+|L*R?)\b/
-
-
# @return true if name is left or right of context
-
1
def child_of? context
-
return false unless junction?
-
context_key = context.to_name.key
-
absolute_name(context).parent_keys.include? context_key
-
end
-
-
1
def relative?
-
423
starts_with_joint? || (s =~ RELATIVE_REGEXP).present?
-
end
-
-
1
def simple_relative?
-
starts_with_joint? && (s =~ RELATIVE_REGEXP).nil?
-
end
-
-
1
def absolute?
-
147
!relative?
-
end
-
-
1
def stripped
-
42
s.gsub RELATIVE_REGEXP, ""
-
end
-
-
1
def starts_with_joint?
-
570
junction? && parts.first.empty?
-
end
-
-
1
def from *from
-
1904
name_from(*from).s
-
end
-
-
# if possible, relativize name into one beginning with a "+". The new name must absolutize back to the correct
-
# original name in the context of "from"
-
1
def name_from *from
-
1904
return self unless (remaining = remove_context *from)
-
336
compressed = remaining.compact.unshift(nil).to_name # exactly one nil at beginning
-
336
key == compressed.absolute_name(from).key ? compressed : self
-
end
-
-
1
def remove_context *from
-
1904
return false unless from.compact.present?
-
546
remaining = parts_excluding *from
-
546
return false if remaining.compact.empty? || # all name parts in context
-
remaining == parts # no name parts in context
-
336
remaining
-
end
-
-
1
def parts_excluding *string
-
546
exclude_name = string.to_name
-
546
exclude_keys = exclude_name ? exclude_name.part_names.map(&:key) : []
-
546
parts_minus exclude_keys
-
end
-
-
1
def parts_minus keys_to_ignore
-
546
parts.map do |part|
-
897
next if part.empty?
-
877
next if part =~ /^_/ # this removes relative parts. why?
-
877
next if keys_to_ignore.member? part.to_name.key
-
516
part
-
end
-
end
-
-
1
def absolute context
-
19499
context = (context || "").to_name
-
19499
new_parts = absolutize_contextual_parts context
-
19499
return "" if new_parts.empty?
-
19493
absolutize_extremes new_parts, context.s
-
19493
new_parts.join self.class.joint
-
end
-
-
1
def absolute_name *args
-
13887
absolute(*args).to_name
-
end
-
-
1
def nth_left n
-
# 1 = left; 2= left of left; 3 = left of left of left....
-
(n >= length ? parts[0] : parts[0..-n - 1]).to_name
-
end
-
-
1
private
-
-
1
def absolutize_contextual_parts context
-
19499
parts.map do |part|
-
25963
case part
-
103
when /^_user$/i then user_part part
-
224
when /^_main$/i then self.class.params[:main_name]
-
486
when /^(_self|_whole|_)$/i then context.s
-
94
when /^_left$/i then context.trunk
-
# note - inconsistent use of left v. trunk
-
when /^_right$/i then context.tag
-
when /^_(\d+)$/i then ordinal_part $~[1].to_i, context
-
when /^_(L*)(R?)$/i then partmap_part $~, context
-
25056
else part
-
end.to_s.strip
-
end
-
end
-
-
1
def user_part part
-
103
self.class.session || part
-
end
-
-
1
def ordinal_part pos, context
-
pos = context.length if pos > context.length
-
context.parts[pos - 1]
-
end
-
-
1
def partmap_part match, context
-
l_s, r_s = match[1].size, !match[2].empty?
-
l_part = context.nth_left l_s
-
r_s ? l_part.tag : l_part.s
-
end
-
-
1
def absolutize_extremes new_parts, context
-
19493
[0, -1].each do |i|
-
38986
next if new_parts[i].present?
-
# following avoids recontextualizing with relative contexts.
-
# Eg, `+A+B+.absolute('+A')` should be +A+B, not +A+A+B.
-
1853
next if new_parts.to_name.send "#{[ :start, :end ][i]}s_with_parts?", context
-
1685
new_parts[i] = context
-
end
-
end
-
-
end
-
end
-
1
class Cardname
-
1
module Manipulate
-
# swap a subname
-
# keys are used for comparison
-
1
def swap old, new
-
old_name = old.to_name
-
new_name = new.to_name
-
return self if old_name.num_parts > num_parts
-
return swap_part(old_name, new_name) if old_name.simple?
-
return self unless include? old_name
-
swap_all_subsequences(old_name, new_name).to_name
-
end
-
-
1
def swap_part oldpart, newpart
-
ensure_simpleness oldpart, "Use 'swap' to swap junctions"
-
-
oldpart = oldpart.to_name
-
newpart = newpart.to_name
-
-
parts.map do |p|
-
oldpart == p ? newpart : p
-
end.to_name
-
end
-
-
1
def swap_piece oldpiece, newpiece
-
oldpiece = oldpiece.to_name
-
newpiece = newpiece.to_name
-
-
return swap_part oldpiece, newpiece if oldpiece.simple?
-
return self unless starts_with_parts?(oldpiece)
-
return newpiece if oldpiece.num_parts == num_parts
-
self.class.new [newpiece, self[oldpiece.num_parts..-1]].flatten
-
end
-
-
1
def num_parts
-
1853
parts.length
-
end
-
-
1
def prepend_joint
-
7
joint = self.class.joint
-
7
self =~ /^#{Regexp.escape joint}/ ? self : (joint + self)
-
end
-
-
1
def sub_in str, with:
-
%i[capitalize downcase].product(%i[pluralize singularize])
-
.inject(str) do |s, (m1, m2)|
-
s.gsub(/\b#{send(m1).send(m2)}\b/, with.send(m1).send(m2))
-
end
-
end
-
-
1
alias_method :to_field, :prepend_joint
-
-
1
private
-
-
1
def swap_all_subsequences oldseq, newseq
-
res = []
-
i = 0
-
while i <= num_parts - oldseq.num_parts
-
# for performance reasons: check first character first then the rest
-
if oldseq.part_keys.first == part_keys[i] &&
-
oldseq.part_keys == part_keys[i, oldseq.num_parts]
-
res += newseq.parts
-
i += oldseq.num_parts
-
else
-
res << parts[i]
-
i += 1
-
end
-
end
-
res += parts[i..-1] if i < num_parts
-
res
-
end
-
-
1
def ensure_simpleness part, msg=nil
-
return if part.to_name.simple?
-
raise StandardError, "'#{part}' has to be simple. #{msg}"
-
end
-
end
-
end
-
1
class Cardname
-
# naming conventions:
-
# methods that end with _name return name objects
-
# the same methods without _name return strings
-
1
module Parts
-
1
attr_reader :parts, :part_keys, :simple
-
-
1
alias_method :to_a, :parts
-
-
1
def parts
-
145114
@parts = Cardname.split_parts s
-
end
-
-
1
def simple
-
101122
@simple = parts.size <= 1
-
end
-
1
alias simple? simple
-
-
# @return true if name has more than one part
-
1
def compound?
-
28425
!simple?
-
end
-
1
alias junction? compound?
-
-
1
def part_keys
-
2786
@part_keys ||= simple ? [simple_key] : parts.map { |p| p.to_name.simple_key }
-
end
-
-
1
def left
-
4118
@left ||= simple? ? nil : parts[0..-2] * self.class.joint
-
end
-
-
1
def right
-
20982
@right ||= simple? ? nil : parts[-1]
-
end
-
-
1
def left_name
-
15482
@left_name ||= left && self.class.new(left)
-
end
-
-
1
def right_name
-
10810
@right_name ||= right && self.class.new(right)
-
end
-
-
1
def left_key
-
@left_key ||= simple? ? nil : part_keys[0..-2] * self.class.joint
-
end
-
-
1
def right_key
-
@right_key ||= simple? ? nil : part_keys.last
-
end
-
-
1
def parents
-
@parents ||= junction? ? [left, right] : []
-
end
-
-
1
def parent_names
-
@parent_names ||= junction? ? [left_name, right_name] : []
-
end
-
-
1
def parent_keys
-
@parent_keys ||= junction? ? [left_key, right_key] : []
-
end
-
-
# Note that all names have a trunk and tag,
-
# but only junctions have left and right
-
-
1
def trunk
-
94
@trunk ||= simple? ? s : left
-
end
-
-
1
def tag
-
8940
@tag ||= simple? ? s : right
-
end
-
-
1
def trunk_name
-
1495
@trunk_name ||= simple? ? self : left_name
-
end
-
-
1
def tag_name
-
712
@tag_name ||= simple? ? self : right_name
-
end
-
-
1
def part_names
-
4006
@part_names ||= parts.map(&:to_name)
-
end
-
-
1
def piece_names
-
@piece_names ||= pieces.map(&:to_name)
-
end
-
-
# self and all ancestors (= parts and recursive lefts)
-
# @example
-
# "A+B+C+D".to_name.pieces
-
# # => ["A", "B", "C", "D", "A+B", "A+B+C", "A+B+C+D"]
-
1
def pieces
-
@pieces ||=
-
if simple?
-
[self]
-
else
-
junction_pieces = []
-
parts[1..-1].inject parts[0] do |left, right|
-
piece = [left, right] * self.class.joint
-
junction_pieces << piece
-
piece
-
end
-
parts + junction_pieces
-
end
-
end
-
-
1
def ancestors
-
@ancestors ||= pieces.reject { |p| p == self}
-
end
-
-
# def + other
-
# self.class.new(parts + other.to_name.parts)
-
# end
-
-
1
def [] *args
-
1853
self.class.new parts[*args]
-
end
-
-
# full support of array methods caused trouble with `flatten` calls
-
# It splits the parts of names in arrays
-
# # name parts can be accessed and manipulated like an array
-
# def method_missing method, *args, &block
-
# if ARRAY_METHODS.include? method # parts.respond_to?(method)
-
# self.class.new parts.send(method, *args, &block)
-
# else
-
# super
-
# end
-
# end
-
#
-
# def respond_to? method, include_private=false
-
# return true if ARRAY_METHODS.include? method
-
# super || parts.respond_to?(method, include_private)
-
# end
-
end
-
end
-
1
class Cardname
-
1
module Predicates
-
1
def valid?
-
137
return true if self.class.nothing_banned?
-
!parts.find do |pt|
-
pt.match self.class.banned_re
-
end
-
end
-
-
# @return true if name starts with the same parts as `prefix`
-
1
def starts_with_parts? *prefix
-
1835
start_name = prefix.to_name
-
1835
start_name == self[0, start_name.num_parts]
-
end
-
1
alias_method :start_with_parts?, :starts_with_parts?
-
-
# @return true if name ends with the same parts as `prefix`
-
1
def ends_with_parts? *suffix
-
18
end_name = suffix.to_name
-
18
end_name == self[-end_name.num_parts..-1]
-
end
-
1
alias_method :end_with_parts?, :ends_with_parts?
-
-
# @return true if name has a chain of parts that equals `subname`
-
1
def include? subname
-
subkey = subname.to_name.key
-
key =~ /(^|#{JOINT_RE})#{Regexp.quote subkey}($|#{JOINT_RE})/
-
end
-
end
-
end
-
1
class Cardname
-
1
module Variants
-
1
def simple_key
-
2094
return "" if empty?
-
2050
decoded
-
.underscore
-
.gsub(/[^#{OK4KEY_RE}]+/, '_')
-
.split(/_+/)
-
.reject(&:empty?)
-
2865
.map { |key| self.class.stable_key(key) }
-
.join('_')
-
end
-
-
1
def url_key
-
10202
@url_key ||= part_names.map do |part_name|
-
358
stripped = part_name.decoded.gsub(/[^#{OK4KEY_RE}]+/, ' ').strip
-
358
stripped.gsub(/[\s\_]+/, '_')
-
end * self.class.joint
-
end
-
-
# safe to be used in HTML as id ('*' and '+' are not allowed),
-
# but the key is no longer unique.
-
# For example "A-XB" and "A+*B" have the same safe_key
-
1
def safe_key
-
6169
@safe_key ||= key.tr('*', 'X').tr self.class.joint, '-'
-
end
-
-
1
def decoded
-
2408
@decoded ||= s.index('&') ? HTMLEntities.new.decode(s) : s
-
end
-
-
1
def to_sym
-
21
s.to_sym
-
end
-
end
-
end
-
1
require "rspec"
-
1
require "capybara-puma"
-
1
require "selenium-webdriver"
-
1
require "chromedriver-helper"
-
1
require "capybara"
-
1
require "database_cleaner"
-
1
require "launchy"
-
1
require "cucumber"
-
1
require "email_spec"
-
1
require "listen"
-
# require "spring"
-
1
require "spring-commands-rspec"
-
1
require "spring-commands-cucumber"
-
-
1
module Decko
-
1
DECKO_GEM_ROOT = File.expand_path("../..", __FILE__)
-
-
1
class << self
-
1
def root
-
Rails.root
-
end
-
-
1
def application
-
8
Rails.application
-
end
-
-
1
def config
-
2
application.config
-
end
-
-
1
def paths
-
4
application.paths
-
end
-
-
1
def gem_root
-
13
DECKO_GEM_ROOT
-
end
-
end
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
require "decko/engine"
-
1
require_relative "config/initializers/sedate_parser"
-
-
1
Bundler.require :default, *Rails.groups
-
-
1
module Decko
-
1
class Application < Rails::Application
-
1
initializer :load_decko_environment_config,
-
before: :load_environment_config, group: :all do
-
1
add_path paths, "lib/decko/config/environments", glob: "#{Rails.env}.rb"
-
1
paths["lib/decko/config/environments"].existent.each do |environment|
-
1
require environment
-
end
-
end
-
-
1
class << self
-
1
def inherited base
-
1
super
-
1
Rails.app_class = base
-
1
add_lib_to_load_path!(find_root(base.called_from))
-
1
ActiveSupport.run_load_hooks(:before_configuration, base.instance)
-
end
-
end
-
-
1
def add_path paths, path, options={}
-
1
root = options.delete(:root) || Decko.gem_root
-
1
options[:with] = File.join(root, (options[:with] || path))
-
1
paths.add path, options
-
end
-
-
1
def config
-
1239
@config ||= begin
-
1
config = super
-
-
1
Cardio.set_config config
-
-
# any config settings below:
-
# (a) do not apply to Card used outside of a Decko context
-
# (b) cannot be overridden in a deck's application.rb, but
-
# (c) CAN be overridden in an environment file
-
-
# therefore, in general, they should be restricted to settings that
-
# (1) are specific to the web environment, and
-
# (2) should not be overridden
-
# ..and we should address (c) above!
-
-
# general card settings (overridable and not) should be in cardio.rb
-
# overridable decko-specific settings don't have a place yet
-
# but should probably follow the cardio pattern.
-
-
# config.load_defaults "6.0"
-
1
config.autoloader = :zeitwerk
-
1
config.load_default = "6.0"
-
1
config.i18n.enforce_available_locales = true
-
# config.active_record.raise_in_transactional_callbacks = true
-
-
1
config.allow_concurrency = false
-
1
config.assets.enabled = false
-
1
config.assets.version = "1.0"
-
-
1
config.filter_parameters += [:password]
-
-
# Rails.autoloaders.log!
-
1
Rails.autoloaders.main.ignore(File.join(Cardio.gem_root, "lib/card/seed_consts.rb"))
-
1
config
-
end
-
end
-
-
1
def paths
-
19
@paths ||= begin
-
1
paths = super
-
1
Cardio.set_paths paths
-
-
1
paths.add "files"
-
-
1
paths["app/models"] = []
-
1
paths["app/mailers"] = []
-
-
1
unless paths["config/routes.rb"].existent.present?
-
add_path paths, "config/routes.rb",
-
with: "rails/application-routes.rb"
-
end
-
-
1
paths
-
end
-
end
-
end
-
end
-
-
1
require "rails/all"
-
1
require "cardio"
-
-
# TODO: Move these to modules that use them
-
1
require "htmlentities"
-
1
require "coderay"
-
1
require "haml"
-
1
require "kaminari"
-
1
require "bootstrap4-kaminari-views"
-
1
require "diff/lcs"
-
1
require "builder"
-
-
1
require "decko"
-
-
1
module Decko
-
1
class Engine < ::Rails::Engine
-
1
paths.add "app/controllers", with: "rails/controllers", eager_load: true
-
1
paths.add "gem-assets", with: "rails/assets"
-
1
paths.add "config/routes.rb", with: "rails/engine-routes.rb"
-
1
paths.add "lib/tasks", with: "#{::Decko.gem_root}/lib/decko/tasks",
-
glob: "**/*.rake"
-
1
paths["lib/tasks"] << "#{::Cardio.gem_root}/lib/card/tasks"
-
1
paths.add "lib/decko/config/initializers",
-
with: File.join(Decko.gem_root, "lib/decko/config/initializers"),
-
glob: "**/*.rb"
-
-
1
initializer "decko.engine.load_config_initializers",
-
after: :load_config_initializers do
-
1
paths["lib/decko/config/initializers"].existent.sort.each do |initializer|
-
3
load(initializer)
-
end
-
end
-
-
1
initializer "engine.copy_configs",
-
before: "decko.engine.load_config_initializers" do
-
# this code should all be in Decko somewhere, and it is now, gem-wize
-
# Ideally railties would do this for us; this is needed for both use cases
-
1
Engine.paths["request_log"] = Decko.paths["request_log"]
-
1
Engine.paths["log"] = Decko.paths["log"]
-
1
Engine.paths["lib/tasks"] = Decko.paths["lib/tasks"]
-
1
Engine.paths["config/routes.rb"] = Decko.paths["config/routes.rb"]
-
end
-
-
1
initializer :connect_on_load do
-
1
ActiveSupport.on_load(:active_record) do
-
1
ActiveRecord::Base.establish_connection(::Rails.env.to_sym)
-
end
-
# ActiveSupport.on_load(:after_initialize) do
-
# # require "card" if Cardio.load_card?
-
# Card if Cardio.load_card?
-
# rescue ActiveRecord::StatementInvalid => e
-
# ::Rails.logger.warn "database not available[#{::Rails.env}] #{e}"
-
# end
-
end
-
end
-
end
-
1
module Decko
-
# methods for managing decko responses
-
1
module Response
-
1
def response_format
-
315
@response_format ||= format_name_from_params
-
end
-
-
1
private
-
-
1
def respond format, result, status
-
315
if status.in? [302, 303]
-
hard_redirect result
-
315
elsif format.is_a?(Card::Format::FileFormat) && status == 200
-
30
send_file(*result)
-
else
-
285
render_response result.to_s.html_safe, status, format.mime_type
-
end
-
end
-
-
1
def render_response body, status, content_type
-
285
render body: body, status: status, content_type: content_type
-
end
-
-
1
def redirect_cud_success success
-
157
redirect_type = success.redirect || default_cud_success_redirect_type
-
157
if redirect_type.to_s == "soft"
-
28
success.target ||= self
-
28
soft_redirect success
-
else
-
129
hard_redirect success.to_url, 303
-
end
-
end
-
-
1
def default_cud_success_redirect_type
-
107
Card::Env.ajax? ? "soft" : "hard"
-
end
-
-
# return a redirect response
-
1
def hard_redirect url, status=302
-
129
url = card_url url # make sure we have absolute url
-
129
if Card::Env.ajax?
-
# lets client reset window location
-
# (not just receive redirected response)
-
# formerly used 303 response, but that gave IE the fits
-
50
render json: { redirect: url }
-
else
-
79
redirect_to url, status: status
-
end
-
end
-
-
# return a standard GET response directly.
-
# Used in AJAX situations where PRG pattern is unwieldy
-
1
def soft_redirect success
-
# Card::Cache.renew
-
28
@card = success.target
-
28
require_card_for_soft_redirect!
-
28
self.params = Card::Env[:params] = soft_redirect_params
-
28
load_action
-
28
show
-
end
-
-
1
def reload
-
render json: { reload: true }
-
end
-
-
1
def soft_redirect_params
-
28
new_params = params.clone
-
28
new_params.delete :card
-
28
new_params.delete :action
-
28
new_params.merge Card::Env.success.params
-
end
-
-
1
def require_card_for_soft_redirect!
-
28
return if card.is_a? Card
-
raise Card::Error, "tried to do soft redirect without a card"
-
end
-
-
# (obviously) deprecated
-
1
def send_deprecated_asset
-
filename = [params[:mark], params[:format]].compact.join(".")
-
send_file asset_file_path(filename), x_sendfile: true
-
end
-
-
1
def asset_file_path filename
-
path = Decko::Engine.paths["gem-assets"].existent.first
-
path = File.join path, filename
-
validate_path filename, path
-
path
-
end
-
-
1
def validate_path filename, path
-
# for security, block relative paths
-
raise Card::Error::BadAddress if filename.include?("../") || !::File.exist?(path)
-
end
-
-
# TODO: everything below should go in a separate file
-
# below is about beginning (initialization). above is about end (response)
-
# Both this file and that would make sense as submodules of CardController
-
-
1
def load_format
-
315
request.format = :html if implicit_html?
-
315
card.format response_format
-
end
-
-
1
def implicit_html?
-
315
(Card::Env.ajax? && !params[:format]) || request.format.to_s == "*/*"
-
end
-
-
1
def format_name_from_params
-
315
if explicit_file_format? then :file
-
285
elsif params[:format].present? then params[:format].to_sym
-
274
else request.format.to_sym
-
end
-
end
-
-
1
def explicit_file_format?
-
315
params[:explicit_file] || !Card::Format.registered.member?(request.format)
-
end
-
-
1
def interpret_mark mark
-
284
case mark
-
when "*previous"
-
# Why support this? It's only needed in Success, right? Deprecate?
-
return hard_redirect(Card::Env.previous_location)
-
when nil
-
71
implicit_mark
-
else
-
213
explicit_mark mark
-
end
-
end
-
-
1
def explicit_mark mark
-
# we should find the place where we produce these bad urls
-
213
mark.valid_encoding? ? mark : mark.force_encoding("ISO-8859-1").encode("UTF-8")
-
end
-
-
1
def implicit_mark
-
case
-
72
when initial_setup then ""
-
43
when (name = params.dig :card, :name) then name
-
11
when view_does_not_require_name? then ""
-
16
else home_mark
-
end
-
end
-
-
1
def home_mark
-
16
Card::Rule.global_setting(:home) || "Home"
-
end
-
-
1
def view_does_not_require_name?
-
27
return false unless (view = params[:view]&.to_sym)
-
11
Card::Set::Format::AbstractFormat::ViewOpts.unknown_ok[view]
-
end
-
-
# alters params
-
1
def initial_setup
-
71
return unless initial_setup?
-
1
prepare_setup_card!
-
end
-
-
1
def initial_setup?
-
71
Card::Auth.needs_setup? && Card::Env.html?
-
end
-
-
1
def prepare_setup_card!
-
1
params[:card] = { type_id: Card.default_accounted_type_id }
-
1
params[:view] = "setup"
-
end
-
end
-
end
-
1
class ApplicationController < ActionController::Base
-
end
-
# -*- encoding : utf-8 -*-
-
-
# Decko's only controller.
-
1
class CardController < ApplicationController
-
1
include ::Card::Env::Location
-
1
include ::Recaptcha::Verify
-
1
include ::Decko::Response
-
-
1
layout nil
-
1
attr_reader :card
-
-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
# PUBLIC METHODS
-
-
1
def create
-
112
handle { card.save! }
-
end
-
-
1
def read
-
284
show
-
end
-
-
1
def update
-
196
card.new_card? ? create : handle { card.update! params[:card] }
-
end
-
-
1
def delete
-
14
handle { card.delete! }
-
end
-
-
# @deprecated
-
1
def asset
-
Rails.logger.info "Routing assets through Card. Recommend symlink from " \
-
'Deck to Card gem using "rake decko:update_assets_symlink"'
-
send_deprecated_asset
-
end
-
-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
# PRIVATE METHODS
-
-
1
private
-
-
#-------( FILTERS )
-
-
1
before_action :setup, except: [:asset]
-
1
before_action :authenticate, except: [:asset]
-
1
before_action :load_mark, only: [:read]
-
1
before_action :load_card, except: [:asset]
-
1
before_action :load_action, only: [:read]
-
1
before_action :refresh_card, only: [:create, :update, :delete]
-
-
1
def setup
-
444
Card::Machine.refresh_script_and_style unless params[:explicit_file]
-
444
Card::Cache.renew
-
444
Card::Env.reset controller: self
-
end
-
-
1
def authenticate
-
444
Card::Auth.signin_with token: params[:token], api_key: params[:api_key]
-
end
-
-
1
def load_mark
-
284
params[:mark] = interpret_mark params[:mark]
-
end
-
-
1
def load_card
-
444
@card = Card.controller_fetch params
-
444
raise Card::Error::NotFound unless card
-
444
record_as_main
-
end
-
-
1
def load_action
-
312
card.select_action_by_params params
-
312
card.content = card.last_draft_content if params[:edit_draft] && card.drafts.present?
-
end
-
-
# TODO: refactor this away this when new layout handling is ready
-
1
def record_as_main
-
444
Card::Env[:main_name] = params[:main] || card&.name || ""
-
end
-
-
1
def refresh_card
-
160
@card = card.refresh
-
end
-
-
# ----------( HELPER METHODS ) -------------
-
-
1
def handle
-
160
Card::Env.success card.name
-
160
yield ? cud_success : raise(Card::Error::UserError)
-
end
-
-
# successful create, update, or delete act
-
1
def cud_success
-
157
success = Card::Env.success.in_context card.name
-
157
if success.reload?
-
reload # instruct JSON to reload
-
else
-
157
redirect_cud_success success
-
end
-
end
-
-
1
def show view=nil, status=200
-
315
card.action = :read
-
315
format = load_format
-
315
result = render_page format, view
-
315
status = format.error_status || status
-
315
respond format, result, status
-
end
-
-
1
def render_page format, view
-
315
view ||= view_from_params
-
315
card.act do
-
315
format.page self, view, Card::Env.slot_opts
-
end
-
end
-
-
1
def view_from_params
-
312
params[:view] || params[:v]
-
end
-
-
1
def handle_exception exception
-
3
raise exception if debug_exception?(exception)
-
3
@card ||= Card.new
-
3
error = Card::Error.report exception, card
-
3
show error.class.view, error.class.status_code
-
end
-
-
# TODO: move to exception object
-
1
def debug_exception? e
-
3
!e.is_a?(Card::Error::UserError) &&
-
!e.is_a?(ActiveRecord::RecordInvalid) &&
-
Card::Codename[:debugger] &&
-
Card[:debugger]&.content =~ /on/ # && !Card::Env.ajax?
-
end
-
-
1
class << self
-
1
def rescue_from_class *klasses
-
1
klasses.each do |klass|
-
8
rescue_from(klass) { |exception| handle_exception exception }
-
end
-
end
-
-
1
def rescue_all?
-
1
Cardio.config.rescue_all_in_controller
-
end
-
end
-
-
1
rescue_from_class(*Card::Error::UserError.user_error_classes)
-
1
rescue_from_class StandardError if rescue_all?
-
end
-
# -*- encoding : utf-8 -*-
-
-
1
Decko::Engine.routes.draw do
-
1
files = Decko::Engine.config.files_web_path
-
1
file_matchers = { mark: /[^-]+/, explicit_file: true, rev_id: /[^-]+/ }
-
-
1
root "card#read"
-
-
# explicit file request
-
1
get({ "#{files}/:mark/:rev_id(-:size).:format" => "card#read" }.merge(file_matchers))
-
-
# DEPRECATED (old file and asset requests)
-
1
get({ "#{files}/:mark(-:size)-:rev_id.:format" => "card#read" }.merge(file_matchers))
-
1
%w[assets javascripts jasmine].each do |prefix|
-
3
get "#{prefix}/*mark" => "card#asset"
-
end
-
-
# Standard GET requests
-
1
get "(/decko)/:mark(.:format)" => "card#read" # /decko is deprecated
-
-
# Alternate GET requests
-
1
get "new/:type" => "card#read", view: "new" # common case for card without mark
-
1
get "type/:type" => "card#read"
-
1
get ":mark/view/:view(.:format)" => "card#read" # simplifies API documentation
-
1
get "card/:view(/:mark(.:format))" => "card#read", view: /new|edit/ # legacy
-
-
# RESTful (without mark)
-
1
post "/" => "card#create"
-
1
put "/" => "card#update"
-
1
patch "/" => "card#update"
-
1
delete "/" => "card#delete"
-
-
# RESTful (with mark)
-
1
match ":mark(.:format)" => "card#create", via: :post
-
1
match ":mark(.:format)" => "card#update", via: %i[put patch]
-
1
match ":mark(.:format)" => "card#delete", via: :delete
-
-
# explicit GET alternatives for transactions
-
1
%w[create read update delete asset].each do |action|
-
5
get "(card)/#{action}(/:mark(.:format))" => "card", action: action
-
end
-
-
# for super-explicit over-achievers
-
1
match "(card)/create(/:mark(.:format))" => "card#create", via: %i[post patch]
-
1
match "(card)/update(/:mark(.:format))" => "card#update", via: %i[post put patch]
-
1
match "(card)/delete(/:mark(.:format))" => "card#delete", via: :delete
-
-
# for super-lazy under-achievers
-
1
get ":mark/:view(.:format)" => "card#read"
-
-
# Wildcard for bad addresses
-
1
get "*mark" => "card#read", view: "bad_address"
-
end